From 86145c1564164c1abe2b678323bb00cc986be329 Mon Sep 17 00:00:00 2001 From: Steve Date: Mon, 8 Sep 2025 12:59:27 -0400 Subject: [PATCH 01/14] chore: removed duplicate descriptions from frontmatter --- content/cairo-contracts/access.mdx | 1 - content/cairo-contracts/accounts.mdx | 1 - content/cairo-contracts/backwards-compatibility.mdx | 1 - content/cairo-contracts/components.mdx | 1 - content/cairo-contracts/erc1155.mdx | 1 - content/cairo-contracts/erc20.mdx | 1 - content/cairo-contracts/erc4626.mdx | 1 - content/cairo-contracts/erc721.mdx | 1 - content/cairo-contracts/finance.mdx | 1 - content/cairo-contracts/governance/governor.mdx | 1 - content/cairo-contracts/governance/multisig.mdx | 1 - content/cairo-contracts/governance/timelock.mdx | 1 - content/cairo-contracts/governance/votes.mdx | 1 - content/cairo-contracts/guides/deploy-udc.mdx | 1 - content/cairo-contracts/guides/deployment.mdx | 1 - content/cairo-contracts/guides/erc20-permit.mdx | 1 - content/cairo-contracts/guides/erc20-supply.mdx | 1 - content/cairo-contracts/guides/interfaces-and-dispatchers.mdx | 1 - content/cairo-contracts/guides/snip12.mdx | 1 - content/cairo-contracts/guides/src5-migration.mdx | 1 - content/cairo-contracts/index.mdx | 1 - content/cairo-contracts/interfaces.mdx | 1 - content/cairo-contracts/introspection.mdx | 1 - content/cairo-contracts/macros.mdx | 1 - content/cairo-contracts/macros/type_hash.mdx | 1 - content/cairo-contracts/macros/with_components.mdx | 1 - content/cairo-contracts/presets.mdx | 1 - content/cairo-contracts/security.mdx | 1 - content/cairo-contracts/udc.mdx | 1 - content/cairo-contracts/upgrades.mdx | 1 - content/cairo-contracts/utils/_class_hashes.mdx | 1 - content/cairo-contracts/utils/_common.mdx | 1 - content/cairo-contracts/wizard.mdx | 1 - content/community-contracts/account-modules.mdx | 1 - content/community-contracts/crosschain.mdx | 1 - content/community-contracts/index.mdx | 1 - content/community-contracts/paymasters.mdx | 1 - content/community-contracts/utilities.mdx | 1 - content/confidential-contracts/index.mdx | 1 - content/confidential-contracts/token.mdx | 1 - content/contracts-compact/access.mdx | 1 - content/contracts-compact/api/access.mdx | 1 - content/contracts-compact/api/fungibleToken.mdx | 1 - content/contracts-compact/api/multitoken.mdx | 1 - content/contracts-compact/api/nonFungibleToken.mdx | 1 - content/contracts-compact/api/ownable.mdx | 1 - content/contracts-compact/api/utils.mdx | 1 - content/contracts-compact/extensibility.mdx | 1 - content/contracts-compact/fungibleToken.mdx | 1 - content/contracts-compact/index.mdx | 1 - content/contracts-compact/multitoken.mdx | 1 - content/contracts-compact/nonFungibleToken.mdx | 1 - content/contracts-compact/ownable.mdx | 1 - content/contracts-compact/utils.mdx | 1 - content/contracts-compact/zkCircuits101.mdx | 1 - content/contracts-stylus/access-control.mdx | 1 - content/contracts-stylus/beacon-proxy.mdx | 1 - content/contracts-stylus/common.mdx | 1 - content/contracts-stylus/crypto.mdx | 1 - content/contracts-stylus/erc1155-burnable.mdx | 1 - content/contracts-stylus/erc1155-metadata-uri.mdx | 1 - content/contracts-stylus/erc1155-pausable.mdx | 1 - content/contracts-stylus/erc1155-supply.mdx | 1 - content/contracts-stylus/erc1155-uri-storage.mdx | 1 - content/contracts-stylus/erc1155.mdx | 1 - content/contracts-stylus/erc1967.mdx | 1 - content/contracts-stylus/erc20-burnable.mdx | 1 - content/contracts-stylus/erc20-capped.mdx | 1 - content/contracts-stylus/erc20-flash-mint.mdx | 1 - content/contracts-stylus/erc20-metadata.mdx | 1 - content/contracts-stylus/erc20-pausable.mdx | 1 - content/contracts-stylus/erc20-permit.mdx | 1 - content/contracts-stylus/erc20-wrapper.mdx | 1 - content/contracts-stylus/erc20.mdx | 1 - content/contracts-stylus/erc2981.mdx | 1 - content/contracts-stylus/erc4626.mdx | 1 - content/contracts-stylus/erc721-burnable.mdx | 1 - content/contracts-stylus/erc721-consecutive.mdx | 1 - content/contracts-stylus/erc721-enumerable.mdx | 1 - content/contracts-stylus/erc721-metadata.mdx | 1 - content/contracts-stylus/erc721-pausable.mdx | 1 - content/contracts-stylus/erc721-uri-storage.mdx | 1 - content/contracts-stylus/erc721-wrapper.mdx | 1 - content/contracts-stylus/erc721.mdx | 1 - content/contracts-stylus/finance.mdx | 1 - content/contracts-stylus/index.mdx | 1 - content/contracts-stylus/proxy.mdx | 1 - content/contracts-stylus/tokens.mdx | 1 - content/contracts-stylus/utilities.mdx | 1 - content/contracts-stylus/uups-proxy.mdx | 1 - content/contracts-stylus/vesting-wallet.mdx | 1 - content/contracts-ui-builder/building-adapters.mdx | 1 - content/contracts-ui-builder/customization.mdx | 1 - content/contracts-ui-builder/exporting-and-history.mdx | 1 - content/contracts-ui-builder/functions.mdx | 1 - content/contracts-ui-builder/index.mdx | 1 - content/contracts-ui-builder/loading-contracts.mdx | 1 - content/contracts-ui-builder/networks.mdx | 1 - content/contracts/v2.x/access-control.mdx | 1 - content/contracts/v2.x/api/GSN.mdx | 1 - content/contracts/v2.x/api/access.mdx | 1 - content/contracts/v2.x/api/cryptography.mdx | 1 - content/contracts/v2.x/api/drafts.mdx | 1 - content/contracts/v2.x/api/introspection.mdx | 1 - content/contracts/v2.x/api/math.mdx | 1 - content/contracts/v2.x/api/payment.mdx | 1 - content/contracts/v2.x/api/presets.mdx | 1 - content/contracts/v2.x/api/proxy.mdx | 1 - content/contracts/v2.x/api/token/ERC1155.mdx | 1 - content/contracts/v2.x/api/token/ERC20.mdx | 1 - content/contracts/v2.x/api/token/ERC721.mdx | 1 - content/contracts/v2.x/api/token/ERC777.mdx | 1 - content/contracts/v2.x/api/utils.mdx | 1 - content/contracts/v2.x/crowdsales.mdx | 1 - content/contracts/v2.x/erc20-supply.mdx | 1 - content/contracts/v2.x/erc20.mdx | 1 - content/contracts/v2.x/erc721.mdx | 1 - content/contracts/v2.x/erc777.mdx | 1 - content/contracts/v2.x/gsn-strategies.mdx | 1 - content/contracts/v2.x/gsn.mdx | 1 - content/contracts/v2.x/index.mdx | 1 - content/contracts/v2.x/releases-stability.mdx | 1 - content/contracts/v2.x/tokens.mdx | 1 - content/contracts/v2.x/utilities.mdx | 1 - content/contracts/v3.x/access-control.mdx | 1 - content/contracts/v3.x/api/GSN.mdx | 1 - content/contracts/v3.x/api/access.mdx | 1 - content/contracts/v3.x/api/cryptography.mdx | 1 - content/contracts/v3.x/api/drafts.mdx | 1 - content/contracts/v3.x/api/introspection.mdx | 1 - content/contracts/v3.x/api/math.mdx | 1 - content/contracts/v3.x/api/payment.mdx | 1 - content/contracts/v3.x/api/presets.mdx | 1 - content/contracts/v3.x/api/proxy.mdx | 1 - content/contracts/v3.x/api/token/ERC1155.mdx | 1 - content/contracts/v3.x/api/token/ERC20.mdx | 1 - content/contracts/v3.x/api/token/ERC721.mdx | 1 - content/contracts/v3.x/api/token/ERC777.mdx | 1 - content/contracts/v3.x/api/utils.mdx | 1 - content/contracts/v3.x/crowdsales.mdx | 1 - content/contracts/v3.x/drafts.mdx | 1 - content/contracts/v3.x/erc1155.mdx | 1 - content/contracts/v3.x/erc20-supply.mdx | 1 - content/contracts/v3.x/erc20.mdx | 1 - content/contracts/v3.x/erc721.mdx | 1 - content/contracts/v3.x/erc777.mdx | 1 - content/contracts/v3.x/extending-contracts.mdx | 1 - content/contracts/v3.x/gsn-strategies.mdx | 1 - content/contracts/v3.x/gsn.mdx | 1 - content/contracts/v3.x/index.mdx | 1 - content/contracts/v3.x/releases-stability.mdx | 1 - content/contracts/v3.x/tokens.mdx | 1 - content/contracts/v3.x/upgradeable.mdx | 1 - content/contracts/v3.x/utilities.mdx | 1 - content/contracts/v4.x/access-control.mdx | 1 - content/contracts/v4.x/crosschain.mdx | 1 - content/contracts/v4.x/crowdsales.mdx | 1 - content/contracts/v4.x/drafts.mdx | 1 - content/contracts/v4.x/erc1155.mdx | 1 - content/contracts/v4.x/erc20-supply.mdx | 1 - content/contracts/v4.x/erc20.mdx | 1 - content/contracts/v4.x/erc4626.mdx | 1 - content/contracts/v4.x/erc721.mdx | 1 - content/contracts/v4.x/erc777.mdx | 1 - content/contracts/v4.x/extending-contracts.mdx | 1 - content/contracts/v4.x/governance.mdx | 1 - content/contracts/v4.x/index.mdx | 1 - content/contracts/v4.x/releases-stability.mdx | 1 - content/contracts/v4.x/tokens.mdx | 1 - content/contracts/v4.x/upgradeable.mdx | 1 - content/contracts/v4.x/utilities.mdx | 1 - content/contracts/v5.x/access-control.mdx | 1 - content/contracts/v5.x/account-abstraction.mdx | 1 - content/contracts/v5.x/accounts.mdx | 1 - content/contracts/v5.x/backwards-compatibility.mdx | 1 - content/contracts/v5.x/eoa-delegation.mdx | 1 - content/contracts/v5.x/erc1155.mdx | 1 - content/contracts/v5.x/erc20-supply.mdx | 1 - content/contracts/v5.x/erc20.mdx | 1 - content/contracts/v5.x/erc4626.mdx | 1 - content/contracts/v5.x/erc6909.mdx | 1 - content/contracts/v5.x/erc721.mdx | 1 - content/contracts/v5.x/extending-contracts.mdx | 1 - content/contracts/v5.x/faq.mdx | 1 - content/contracts/v5.x/governance.mdx | 1 - content/contracts/v5.x/learn/building-a-dapp.mdx | 1 - .../contracts/v5.x/learn/connecting-to-public-test-networks.mdx | 1 - content/contracts/v5.x/learn/deploying-and-interacting.mdx | 1 - content/contracts/v5.x/learn/developing-smart-contracts.mdx | 1 - content/contracts/v5.x/learn/index.mdx | 1 - content/contracts/v5.x/learn/preparing-for-mainnet.mdx | 1 - content/contracts/v5.x/learn/sending-gasless-transactions.mdx | 1 - content/contracts/v5.x/learn/setting-up-a-node-project.mdx | 1 - content/contracts/v5.x/learn/upgrading-smart-contracts.mdx | 1 - content/contracts/v5.x/learn/writing-automated-tests.mdx | 1 - content/contracts/v5.x/multisig.mdx | 1 - content/contracts/v5.x/tokens.mdx | 1 - content/contracts/v5.x/upgradeable.mdx | 1 - content/contracts/v5.x/wizard.mdx | 1 - content/defender/dac.mdx | 1 - content/defender/faq.mdx | 1 - content/defender/guide/factory-monitor.mdx | 1 - content/defender/guide/fireblock-defender-integration.mdx | 1 - content/defender/guide/forked-network.mdx | 1 - content/defender/guide/meta-tx.mdx | 1 - content/defender/guide/private-network.mdx | 1 - content/defender/guide/timelock-roles.mdx | 1 - content/defender/guide/upgrade-actions-dependencies.mdx | 1 - content/defender/guide/usage-notification.mdx | 1 - content/defender/index.mdx | 1 - content/defender/integrations.mdx | 1 - content/defender/logs.mdx | 1 - content/defender/module/access-control.mdx | 1 - content/defender/module/actions.mdx | 1 - content/defender/module/address-book.mdx | 1 - content/defender/module/audit.mdx | 1 - content/defender/module/code.mdx | 1 - content/defender/module/deploy.mdx | 1 - content/defender/module/monitor.mdx | 1 - content/defender/module/relayers.mdx | 1 - content/defender/module/transaction-proposals.mdx | 1 - content/defender/remix-plugin.mdx | 1 - content/defender/sdk.mdx | 1 - content/defender/settings.mdx | 1 - content/defender/settings/notifications.mdx | 1 - content/defender/tutorial/access-control.mdx | 1 - content/defender/tutorial/actions.mdx | 1 - content/defender/tutorial/deploy.mdx | 1 - content/defender/tutorial/monitor.mdx | 1 - content/defender/tutorial/relayer.mdx | 1 - content/defender/tutorial/workflows.mdx | 1 - content/defender/wizard-plugin.mdx | 1 - content/openzeppelin-monitor/architecture.mdx | 1 - content/openzeppelin-monitor/contribution.mdx | 1 - content/openzeppelin-monitor/error.mdx | 1 - content/openzeppelin-monitor/index.mdx | 1 - content/openzeppelin-monitor/project-structure.mdx | 1 - content/openzeppelin-monitor/quickstart.mdx | 1 - content/openzeppelin-monitor/rpc.mdx | 1 - content/openzeppelin-monitor/scripts.mdx | 1 - content/openzeppelin-monitor/testing.mdx | 1 - content/openzeppelin-relayer/configuration/index.mdx | 1 - content/openzeppelin-relayer/configuration/signers.mdx | 1 - content/openzeppelin-relayer/configuration/storage.mdx | 1 - content/openzeppelin-relayer/evm.mdx | 1 - content/openzeppelin-relayer/index.mdx | 1 - content/openzeppelin-relayer/network_configuration.mdx | 1 - content/openzeppelin-relayer/plugins.mdx | 1 - content/openzeppelin-relayer/quickstart.mdx | 1 - content/openzeppelin-relayer/roadmap.mdx | 1 - content/openzeppelin-relayer/solana.mdx | 1 - content/openzeppelin-relayer/stellar.mdx | 1 - content/openzeppelin-relayer/structure.mdx | 1 - content/stellar-contracts/access-control.mdx | 1 - content/stellar-contracts/get-started.mdx | 1 - content/stellar-contracts/helpers/default-impl-macro.mdx | 1 - content/stellar-contracts/index.mdx | 1 - content/stellar-contracts/ownable.mdx | 1 - content/stellar-contracts/tokens/fungible/fungible.mdx | 1 - content/stellar-contracts/tokens/fungible/sac-admin-generic.mdx | 1 - content/stellar-contracts/tokens/fungible/sac-admin-wrapper.mdx | 1 - .../stellar-contracts/tokens/non-fungible/nft-consecutive.mdx | 1 - content/stellar-contracts/tokens/non-fungible/nft-enumerable.mdx | 1 - content/stellar-contracts/tokens/non-fungible/non-fungible.mdx | 1 - content/stellar-contracts/utils/crypto.mdx | 1 - content/stellar-contracts/utils/pausable.mdx | 1 - content/stellar-contracts/utils/upgradeable.mdx | 1 - content/substrate-runtimes/glossary.mdx | 1 - content/substrate-runtimes/guides/async_backing.mdx | 1 - content/substrate-runtimes/guides/contract_migration.mdx | 1 - content/substrate-runtimes/guides/hrmp_channels.mdx | 1 - content/substrate-runtimes/guides/pallet_abstractions.mdx | 1 - content/substrate-runtimes/guides/pay_dot_as_a_fee.mdx | 1 - content/substrate-runtimes/guides/predeployed_contracts.mdx | 1 - content/substrate-runtimes/guides/quick_start.mdx | 1 - content/substrate-runtimes/guides/rpc_differences.mdx | 1 - content/substrate-runtimes/guides/testing_with_zombienet.mdx | 1 - content/substrate-runtimes/guides/weights_fees.mdx | 1 - content/substrate-runtimes/index.mdx | 1 - content/substrate-runtimes/misc/multisig-accounts.mdx | 1 - content/substrate-runtimes/pallets/assets.mdx | 1 - content/substrate-runtimes/pallets/aura_ext.mdx | 1 - content/substrate-runtimes/pallets/balances.mdx | 1 - content/substrate-runtimes/pallets/collator-selection.mdx | 1 - content/substrate-runtimes/pallets/message-queue.mdx | 1 - content/substrate-runtimes/pallets/multisig.mdx | 1 - content/substrate-runtimes/pallets/parachain-system.mdx | 1 - content/substrate-runtimes/pallets/proxy.mdx | 1 - content/substrate-runtimes/pallets/transaction_payment.mdx | 1 - content/substrate-runtimes/pallets/treasury.mdx | 1 - content/substrate-runtimes/pallets/xcm.mdx | 1 - content/substrate-runtimes/pallets/xcmp-queue.mdx | 1 - content/substrate-runtimes/runtime/xcm_executor.mdx | 1 - content/substrate-runtimes/runtimes/evm.mdx | 1 - content/substrate-runtimes/runtimes/generic.mdx | 1 - content/uniswap-hooks/base.mdx | 1 - content/uniswap-hooks/fee.mdx | 1 - content/uniswap-hooks/index.mdx | 1 - content/uniswap-hooks/utilities.mdx | 1 - content/upgrade-plugins/api-core.mdx | 1 - content/upgrade-plugins/api-hardhat-upgrades.mdx | 1 - content/upgrade-plugins/api-truffle-upgrades.mdx | 1 - content/upgrade-plugins/defender-deploy.mdx | 1 - content/upgrade-plugins/faq.mdx | 1 - content/upgrade-plugins/foundry-defender.mdx | 1 - content/upgrade-plugins/foundry/api/api-foundry-upgrades.mdx | 1 - content/upgrade-plugins/foundry/foundry-defender.mdx | 1 - content/upgrade-plugins/foundry/foundry-upgrades.mdx | 1 - content/upgrade-plugins/hardhat-upgrades.mdx | 1 - content/upgrade-plugins/index.mdx | 1 - content/upgrade-plugins/migrate-from-cli.mdx | 1 - content/upgrade-plugins/network-files.mdx | 1 - content/upgrade-plugins/proxies.mdx | 1 - content/upgrade-plugins/truffle-upgrades.mdx | 1 - content/upgrade-plugins/writing-upgradeable.mdx | 1 - 315 files changed, 315 deletions(-) diff --git a/content/cairo-contracts/access.mdx b/content/cairo-contracts/access.mdx index 5ffe42eb..90675363 100644 --- a/content/cairo-contracts/access.mdx +++ b/content/cairo-contracts/access.mdx @@ -1,6 +1,5 @@ --- title: Access -description: Access --- Access control--that is, "who is allowed to do this thing"—is incredibly important in the world of smart contracts. diff --git a/content/cairo-contracts/accounts.mdx b/content/cairo-contracts/accounts.mdx index 8c0d7c78..620a1c4b 100644 --- a/content/cairo-contracts/accounts.mdx +++ b/content/cairo-contracts/accounts.mdx @@ -1,6 +1,5 @@ --- title: Accounts -description: Accounts --- Unlike Ethereum where accounts are derived from a private key, all Starknet accounts are contracts. This means there’s no Externally Owned Account (EOA) diff --git a/content/cairo-contracts/backwards-compatibility.mdx b/content/cairo-contracts/backwards-compatibility.mdx index 4729b29c..1b2d1bf1 100644 --- a/content/cairo-contracts/backwards-compatibility.mdx +++ b/content/cairo-contracts/backwards-compatibility.mdx @@ -1,6 +1,5 @@ --- title: Backwards Compatibility -description: Backwards Compatibility --- OpenZeppelin Contracts uses semantic versioning to communicate backwards compatibility of its API and storage layout. Patch and minor updates will generally be backwards compatible, with rare exceptions as detailed below. Major updates should be assumed incompatible with previous releases. On this page, we provide details about these guarantees. diff --git a/content/cairo-contracts/components.mdx b/content/cairo-contracts/components.mdx index 75873e02..da050fce 100644 --- a/content/cairo-contracts/components.mdx +++ b/content/cairo-contracts/components.mdx @@ -1,6 +1,5 @@ --- title: Components -description: Components --- The following documentation provides reasoning and examples on how to use Contracts for Cairo components. diff --git a/content/cairo-contracts/erc1155.mdx b/content/cairo-contracts/erc1155.mdx index 9140e7d7..fa4dfa35 100644 --- a/content/cairo-contracts/erc1155.mdx +++ b/content/cairo-contracts/erc1155.mdx @@ -1,6 +1,5 @@ --- title: ERC1155 -description: ERC1155 --- The ERC1155 multi-token standard is a specification for [fungibility-agnostic](https://docs.openzeppelin.com/contracts/5.x/tokens#different-kinds-of-tokens) token contracts. diff --git a/content/cairo-contracts/erc20.mdx b/content/cairo-contracts/erc20.mdx index 33955ae7..2509aec2 100644 --- a/content/cairo-contracts/erc20.mdx +++ b/content/cairo-contracts/erc20.mdx @@ -1,6 +1,5 @@ --- title: ERC20 -description: ERC20 --- The ERC20 token standard is a specification for [fungible tokens](https://docs.openzeppelin.com/contracts/4.x/tokens#different-kinds-of-tokens), a type of token where all the units are exactly equal to each other. diff --git a/content/cairo-contracts/erc4626.mdx b/content/cairo-contracts/erc4626.mdx index 26f2767a..30567bff 100644 --- a/content/cairo-contracts/erc4626.mdx +++ b/content/cairo-contracts/erc4626.mdx @@ -1,6 +1,5 @@ --- title: ERC4626 -description: ERC4626 --- [ERC4626](https://eips.ethereum.org/EIPS/eip-4626) is an extension of [ERC20](erc20.mdx) that proposes a standard interface for token vaults. This standard interface can be used by widely different contracts (including lending markets, aggregators, and intrinsically interest bearing tokens), which brings a number of subtleties. Navigating these potential issues is essential to implementing a compliant and composable token vault. diff --git a/content/cairo-contracts/erc721.mdx b/content/cairo-contracts/erc721.mdx index e3f0f47f..334e2ce5 100644 --- a/content/cairo-contracts/erc721.mdx +++ b/content/cairo-contracts/erc721.mdx @@ -1,6 +1,5 @@ --- title: ERC721 -description: ERC721 --- The ERC721 token standard is a specification for [non-fungible tokens](https://docs.openzeppelin.com/contracts/5.x/tokens#different-kinds-of-tokens), or more colloquially: NFTs. diff --git a/content/cairo-contracts/finance.mdx b/content/cairo-contracts/finance.mdx index 78a0de17..84e088da 100644 --- a/content/cairo-contracts/finance.mdx +++ b/content/cairo-contracts/finance.mdx @@ -1,6 +1,5 @@ --- title: Finance -description: Finance --- This module includes primitives for financial systems. diff --git a/content/cairo-contracts/governance/governor.mdx b/content/cairo-contracts/governance/governor.mdx index e22f4e13..a9b6525e 100644 --- a/content/cairo-contracts/governance/governor.mdx +++ b/content/cairo-contracts/governance/governor.mdx @@ -1,6 +1,5 @@ --- title: Governor -description: Governor --- Decentralized protocols are in constant evolution from the moment they are publicly released. Often, diff --git a/content/cairo-contracts/governance/multisig.mdx b/content/cairo-contracts/governance/multisig.mdx index fc15c48b..ca2fdf01 100644 --- a/content/cairo-contracts/governance/multisig.mdx +++ b/content/cairo-contracts/governance/multisig.mdx @@ -1,6 +1,5 @@ --- title: Multisig -description: Multisig --- The Multisig component implements a multi-signature mechanism to enhance the security and diff --git a/content/cairo-contracts/governance/timelock.mdx b/content/cairo-contracts/governance/timelock.mdx index ac9cd466..8b7e078a 100644 --- a/content/cairo-contracts/governance/timelock.mdx +++ b/content/cairo-contracts/governance/timelock.mdx @@ -1,6 +1,5 @@ --- title: Timelock Controller -description: Timelock Controller --- The Timelock Controller provides a means of enforcing time delays on the execution of transactions. This is considered good practice regarding governance systems because it allows users the opportunity to exit the system if they disagree with a decision before it is executed. diff --git a/content/cairo-contracts/governance/votes.mdx b/content/cairo-contracts/governance/votes.mdx index 96d2c874..c52afb77 100644 --- a/content/cairo-contracts/governance/votes.mdx +++ b/content/cairo-contracts/governance/votes.mdx @@ -1,6 +1,5 @@ --- title: Votes -description: Votes --- The [VotesComponent](api/governance.mdx#VotesComponent) provides a flexible system for tracking and delegating voting power. This system allows users to delegate their voting power to other addresses, enabling more active participation in governance. diff --git a/content/cairo-contracts/guides/deploy-udc.mdx b/content/cairo-contracts/guides/deploy-udc.mdx index d51c51e9..879aa5ec 100644 --- a/content/cairo-contracts/guides/deploy-udc.mdx +++ b/content/cairo-contracts/guides/deploy-udc.mdx @@ -1,6 +1,5 @@ --- title: UDC Appchain Deployment -description: UDC Appchain Deployment --- While the Universal Deployer Contract (UDC) is deployed on Starknet public networks, appchains may need to deploy diff --git a/content/cairo-contracts/guides/deployment.mdx b/content/cairo-contracts/guides/deployment.mdx index b34309ff..aa3b04b0 100644 --- a/content/cairo-contracts/guides/deployment.mdx +++ b/content/cairo-contracts/guides/deployment.mdx @@ -1,6 +1,5 @@ --- title: Counterfactual deployments -description: Counterfactual deployments --- A counterfactual contract is a contract we can interact with even before actually deploying it on-chain. diff --git a/content/cairo-contracts/guides/erc20-permit.mdx b/content/cairo-contracts/guides/erc20-permit.mdx index 5bded8ad..c00a6f6e 100644 --- a/content/cairo-contracts/guides/erc20-permit.mdx +++ b/content/cairo-contracts/guides/erc20-permit.mdx @@ -1,6 +1,5 @@ --- title: ERC20Permit -description: ERC20Permit --- The [EIP-2612](https://eips.ethereum.org/EIPS/eip-2612) standard, commonly referred to as ERC20Permit, is designed to support gasless token approvals. This is achieved with an off-chain diff --git a/content/cairo-contracts/guides/erc20-supply.mdx b/content/cairo-contracts/guides/erc20-supply.mdx index d90dd2d1..3de3c072 100644 --- a/content/cairo-contracts/guides/erc20-supply.mdx +++ b/content/cairo-contracts/guides/erc20-supply.mdx @@ -1,6 +1,5 @@ --- title: Creating ERC20 Supply -description: Creating ERC20 Supply --- The standard interface implemented by tokens built on Starknet comes from the popular token standard on Ethereum called ERC20. diff --git a/content/cairo-contracts/guides/interfaces-and-dispatchers.mdx b/content/cairo-contracts/guides/interfaces-and-dispatchers.mdx index fc2cb51c..8169314a 100644 --- a/content/cairo-contracts/guides/interfaces-and-dispatchers.mdx +++ b/content/cairo-contracts/guides/interfaces-and-dispatchers.mdx @@ -1,6 +1,5 @@ --- title: Interfaces and Dispatchers -description: Interfaces and Dispatchers --- This section describes the interfaces OpenZeppelin Contracts for Cairo offer, and explains the design choices behind them. diff --git a/content/cairo-contracts/guides/snip12.mdx b/content/cairo-contracts/guides/snip12.mdx index c4608481..add50929 100644 --- a/content/cairo-contracts/guides/snip12.mdx +++ b/content/cairo-contracts/guides/snip12.mdx @@ -1,6 +1,5 @@ --- title: SNIP12 and Typed Messages -description: SNIP12 and Typed Messages --- Similar to [EIP712](https://eips.ethereum.org/EIPS/eip-712), [SNIP12](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-12.md) is a standard for secure off-chain signature verification on Starknet. diff --git a/content/cairo-contracts/guides/src5-migration.mdx b/content/cairo-contracts/guides/src5-migration.mdx index c9ac4e62..ac880a2a 100644 --- a/content/cairo-contracts/guides/src5-migration.mdx +++ b/content/cairo-contracts/guides/src5-migration.mdx @@ -1,6 +1,5 @@ --- title: Migrating ERC165 to SRC5 -description: Migrating ERC165 to SRC5 --- In the smart contract ecosystem, having the ability to query if a contract supports a given interface is an extremely important feature. diff --git a/content/cairo-contracts/index.mdx b/content/cairo-contracts/index.mdx index 66d18df9..e1165de1 100644 --- a/content/cairo-contracts/index.mdx +++ b/content/cairo-contracts/index.mdx @@ -1,6 +1,5 @@ --- title: Contracts for Cairo -description: Contracts for Cairo --- **A library for secure smart contract development** written in Cairo for [Starknet](https://starkware.co/product/starknet/). This library consists of a set of [reusable components](components.mdx) to build custom smart contracts, as well as diff --git a/content/cairo-contracts/interfaces.mdx b/content/cairo-contracts/interfaces.mdx index b04165db..c56e74cc 100644 --- a/content/cairo-contracts/interfaces.mdx +++ b/content/cairo-contracts/interfaces.mdx @@ -1,6 +1,5 @@ --- title: Interfaces -description: Interfaces --- Starting from version `3.x.x`, OpenZeppelin Contracts for Cairo interfaces have been separated from their implementation modules into a dedicated package called `openzeppelin_interfaces`. This architectural change brings several important benefits: diff --git a/content/cairo-contracts/introspection.mdx b/content/cairo-contracts/introspection.mdx index dedbe132..b8a6f031 100644 --- a/content/cairo-contracts/introspection.mdx +++ b/content/cairo-contracts/introspection.mdx @@ -1,6 +1,5 @@ --- title: Introspection -description: Introspection --- To smooth interoperability, often standards require smart contracts to implement [introspection mechanisms](https://en.wikipedia.org/wiki/Type_introspection). diff --git a/content/cairo-contracts/macros.mdx b/content/cairo-contracts/macros.mdx index 7a8a5182..74d3a608 100644 --- a/content/cairo-contracts/macros.mdx +++ b/content/cairo-contracts/macros.mdx @@ -1,6 +1,5 @@ --- title: Macros -description: Macros --- This crate provides a collection of macros that streamline and simplify development with the library. diff --git a/content/cairo-contracts/macros/type_hash.mdx b/content/cairo-contracts/macros/type_hash.mdx index 98012c38..d4711722 100644 --- a/content/cairo-contracts/macros/type_hash.mdx +++ b/content/cairo-contracts/macros/type_hash.mdx @@ -1,6 +1,5 @@ --- title: type_hash -description: type_hash --- This macro generates a SNIP-12-compatible type hash for a given struct or enum. diff --git a/content/cairo-contracts/macros/with_components.mdx b/content/cairo-contracts/macros/with_components.mdx index cd9fbf5f..8b980a8d 100644 --- a/content/cairo-contracts/macros/with_components.mdx +++ b/content/cairo-contracts/macros/with_components.mdx @@ -1,6 +1,5 @@ --- title: with_components -description: with_components --- This macro simplifies the syntax for adding a set of components to a contract. It: diff --git a/content/cairo-contracts/presets.mdx b/content/cairo-contracts/presets.mdx index 039c868c..7c444c3b 100644 --- a/content/cairo-contracts/presets.mdx +++ b/content/cairo-contracts/presets.mdx @@ -1,6 +1,5 @@ --- title: Presets -description: Presets --- Presets are ready-to-deploy contracts provided by the library. Since presets are intended to be very simple diff --git a/content/cairo-contracts/security.mdx b/content/cairo-contracts/security.mdx index bd54526e..8e165a6b 100644 --- a/content/cairo-contracts/security.mdx +++ b/content/cairo-contracts/security.mdx @@ -1,6 +1,5 @@ --- title: Security -description: Security --- The following documentation provides context, reasoning, and examples of modules found under `openzeppelin_security`. diff --git a/content/cairo-contracts/udc.mdx b/content/cairo-contracts/udc.mdx index 1cf9b9ee..65737530 100644 --- a/content/cairo-contracts/udc.mdx +++ b/content/cairo-contracts/udc.mdx @@ -1,6 +1,5 @@ --- title: Universal Deployer Contract -description: Universal Deployer Contract --- The Universal Deployer Contract (UDC) is a singleton smart contract that wraps the [deploy syscall](https://docs.starknet.io/architecture-and-concepts/smart-contracts/system-calls-cairo1/#deploy) to expose it to any contract that doesn’t implement it, such as account contracts. You can think of it as a standardized generic factory for Starknet contracts. diff --git a/content/cairo-contracts/upgrades.mdx b/content/cairo-contracts/upgrades.mdx index 86cf65e2..49b78bf9 100644 --- a/content/cairo-contracts/upgrades.mdx +++ b/content/cairo-contracts/upgrades.mdx @@ -1,6 +1,5 @@ --- title: Upgrades -description: Upgrades --- In different blockchains, multiple patterns have been developed for making a contract upgradeable including the widely adopted proxy patterns. diff --git a/content/cairo-contracts/utils/_class_hashes.mdx b/content/cairo-contracts/utils/_class_hashes.mdx index 38da5eb8..57d019e6 100644 --- a/content/cairo-contracts/utils/_class_hashes.mdx +++ b/content/cairo-contracts/utils/_class_hashes.mdx @@ -1,5 +1,4 @@ --- title: _class_hashes -description: _class_hashes --- diff --git a/content/cairo-contracts/utils/_common.mdx b/content/cairo-contracts/utils/_common.mdx index b0b3f8c2..41f52f69 100644 --- a/content/cairo-contracts/utils/_common.mdx +++ b/content/cairo-contracts/utils/_common.mdx @@ -1,5 +1,4 @@ --- title: _common -description: _common --- diff --git a/content/cairo-contracts/wizard.mdx b/content/cairo-contracts/wizard.mdx index 82c0e8ec..68c6ac1b 100644 --- a/content/cairo-contracts/wizard.mdx +++ b/content/cairo-contracts/wizard.mdx @@ -1,6 +1,5 @@ --- title: Wizard for Cairo -description: Wizard for Cairo --- Not sure where to start? Use the interactive generator below to bootstrap your diff --git a/content/community-contracts/account-modules.mdx b/content/community-contracts/account-modules.mdx index ac23e1c1..ba7adfa2 100644 --- a/content/community-contracts/account-modules.mdx +++ b/content/community-contracts/account-modules.mdx @@ -1,6 +1,5 @@ --- title: Account Modules -description: Account Modules --- Smart accounts built with [ERC-7579](https://eips.ethereum.org/EIPS/eip-7579) provide a standardized way to extend account functionality through modules (i.e. smart contract instances). This architecture allows accounts to support various features that are compatible with a wide variety of account implementations. See [compatible modules](https://erc7579.com/modules). diff --git a/content/community-contracts/crosschain.mdx b/content/community-contracts/crosschain.mdx index 565b85eb..965b778f 100644 --- a/content/community-contracts/crosschain.mdx +++ b/content/community-contracts/crosschain.mdx @@ -1,6 +1,5 @@ --- title: Cross-chain messaging -description: Cross-chain messaging --- Developers building contracts may require cross-chain functionality. To accomplish this, multiple protocols have implemented their own ways to process operations across chains. diff --git a/content/community-contracts/index.mdx b/content/community-contracts/index.mdx index 83eb235c..ecc38b02 100644 --- a/content/community-contracts/index.mdx +++ b/content/community-contracts/index.mdx @@ -1,6 +1,5 @@ --- title: Community Contracts -description: Community Contracts --- **A community-driven extension of our [Solidity library](https://docs.openzeppelin.com/contracts)**: the gold-standard of smart contract development. This library includes: diff --git a/content/community-contracts/paymasters.mdx b/content/community-contracts/paymasters.mdx index 211ad548..795c5102 100644 --- a/content/community-contracts/paymasters.mdx +++ b/content/community-contracts/paymasters.mdx @@ -1,6 +1,5 @@ --- title: Paymasters -description: Paymasters --- In case you want to sponsor user operations for your users, ERC-4337 defines a special type of contract called _paymaster_, whose purpose is to pay the gas fees consumed by the user operation. diff --git a/content/community-contracts/utilities.mdx b/content/community-contracts/utilities.mdx index b1b511f9..f10b0111 100644 --- a/content/community-contracts/utilities.mdx +++ b/content/community-contracts/utilities.mdx @@ -1,6 +1,5 @@ --- title: Utilities -description: Utilities --- Multiple libraries and general purpose utilities included in the community version of OpenZeppelin Contracts. These are only a set of utility contracts. For the full list, check out the [API Reference](contracts/v5.x/api/utils.mdx). diff --git a/content/confidential-contracts/index.mdx b/content/confidential-contracts/index.mdx index 86a039ef..597ad387 100644 --- a/content/confidential-contracts/index.mdx +++ b/content/confidential-contracts/index.mdx @@ -1,6 +1,5 @@ --- title: Confidential Contracts -description: Confidential Contracts --- A library of smart contracts that use ciphertext for amount, allowing for a wide variety of confidential use-cases, such as confidential tokens, auctions, vesting, voting etc. While the contracts are not written in an opinionated method (other than using the standard encrypted values published by Zama), for testing and examples in the documentation, the [Zama fhEVM](https://github.com/zama-ai/fhevm-solidity) will be used to operate on and decrypt [FHE](https://www.zama.ai/introduction-to-homomorphic-encryption) ciphertext. diff --git a/content/confidential-contracts/token.mdx b/content/confidential-contracts/token.mdx index 92ed36df..2d100471 100644 --- a/content/confidential-contracts/token.mdx +++ b/content/confidential-contracts/token.mdx @@ -1,6 +1,5 @@ --- title: Confidential Fungible Token -description: Confidential Fungible Token --- The [Confidential Fungible Token](contracts/v5.x/api/token.mdx#ConfidentialFungibleToken) is a standard fungible token implementation that is similar to `ERC20`, but built from the ground up with confidentiality in mind. All balance and transfer amounts are represented as ciphertext handles, ensuring that no data is leaked to the public. diff --git a/content/contracts-compact/access.mdx b/content/contracts-compact/access.mdx index 5caae973..59be89aa 100644 --- a/content/contracts-compact/access.mdx +++ b/content/contracts-compact/access.mdx @@ -1,6 +1,5 @@ --- title: Access Control -description: Access Control --- An unshielded Access Control library. diff --git a/content/contracts-compact/api/access.mdx b/content/contracts-compact/api/access.mdx index f1b161f2..2adb5e47 100644 --- a/content/contracts-compact/api/access.mdx +++ b/content/contracts-compact/api/access.mdx @@ -1,6 +1,5 @@ --- title: AccessControl -description: AccessControl --- This page provides the full AccessControl module API. diff --git a/content/contracts-compact/api/fungibleToken.mdx b/content/contracts-compact/api/fungibleToken.mdx index e0a0e294..cda3cdbf 100644 --- a/content/contracts-compact/api/fungibleToken.mdx +++ b/content/contracts-compact/api/fungibleToken.mdx @@ -1,6 +1,5 @@ --- title: FungibleToken -description: FungibleToken --- This module provides the full FungibleToken module API. diff --git a/content/contracts-compact/api/multitoken.mdx b/content/contracts-compact/api/multitoken.mdx index 4b8c9720..c7e42b6c 100644 --- a/content/contracts-compact/api/multitoken.mdx +++ b/content/contracts-compact/api/multitoken.mdx @@ -1,6 +1,5 @@ --- title: MultiToken -description: MultiToken --- This module provides the full MultiToken module API. diff --git a/content/contracts-compact/api/nonFungibleToken.mdx b/content/contracts-compact/api/nonFungibleToken.mdx index 400a5790..a732722f 100644 --- a/content/contracts-compact/api/nonFungibleToken.mdx +++ b/content/contracts-compact/api/nonFungibleToken.mdx @@ -1,6 +1,5 @@ --- title: NonFungibleToken -description: NonFungibleToken --- This module provides the full NonFungibleToken module API. diff --git a/content/contracts-compact/api/ownable.mdx b/content/contracts-compact/api/ownable.mdx index 75b37cf3..5709965c 100644 --- a/content/contracts-compact/api/ownable.mdx +++ b/content/contracts-compact/api/ownable.mdx @@ -1,6 +1,5 @@ --- title: Ownable -description: Ownable --- This module provides the full Ownable module API. diff --git a/content/contracts-compact/api/utils.mdx b/content/contracts-compact/api/utils.mdx index 3b91a2d0..caa7ddb8 100644 --- a/content/contracts-compact/api/utils.mdx +++ b/content/contracts-compact/api/utils.mdx @@ -1,6 +1,5 @@ --- title: Utils -description: Utils --- This package provides the API for all Utils modules. diff --git a/content/contracts-compact/extensibility.mdx b/content/contracts-compact/extensibility.mdx index 6024b397..5c54f733 100644 --- a/content/contracts-compact/extensibility.mdx +++ b/content/contracts-compact/extensibility.mdx @@ -1,6 +1,5 @@ --- title: Extensibility -description: Extensibility --- ## The Module/Contract Pattern diff --git a/content/contracts-compact/fungibleToken.mdx b/content/contracts-compact/fungibleToken.mdx index 918ff699..428868c6 100644 --- a/content/contracts-compact/fungibleToken.mdx +++ b/content/contracts-compact/fungibleToken.mdx @@ -1,6 +1,5 @@ --- title: FungibleToken -description: FungibleToken --- FungibleToken is a specification for [fungible tokens](https://docs.openzeppelin.com/contracts/5.x/tokens#different-kinds-of-tokens), diff --git a/content/contracts-compact/index.mdx b/content/contracts-compact/index.mdx index 83d4994d..81f86b35 100644 --- a/content/contracts-compact/index.mdx +++ b/content/contracts-compact/index.mdx @@ -1,6 +1,5 @@ --- title: Contracts for Compact -description: Contracts for Compact --- **A library for secure smart contract development** written in Compact for [Midnight](https://midnight.network/). diff --git a/content/contracts-compact/multitoken.mdx b/content/contracts-compact/multitoken.mdx index 4466051a..9a78af20 100644 --- a/content/contracts-compact/multitoken.mdx +++ b/content/contracts-compact/multitoken.mdx @@ -1,6 +1,5 @@ --- title: MultiToken -description: MultiToken --- MultiToken is a specification for contracts that manage multiple token types. diff --git a/content/contracts-compact/nonFungibleToken.mdx b/content/contracts-compact/nonFungibleToken.mdx index 35de1fdf..45a80585 100644 --- a/content/contracts-compact/nonFungibleToken.mdx +++ b/content/contracts-compact/nonFungibleToken.mdx @@ -1,6 +1,5 @@ --- title: NonFungibleToken -description: NonFungibleToken --- NonFungibleToken is a specification for [non-fungible tokens](https://docs.openzeppelin.com/contracts/5.x/tokens#different-kinds-of-tokens), diff --git a/content/contracts-compact/ownable.mdx b/content/contracts-compact/ownable.mdx index 1ff19f9c..0d28eaea 100644 --- a/content/contracts-compact/ownable.mdx +++ b/content/contracts-compact/ownable.mdx @@ -1,6 +1,5 @@ --- title: Ownable -description: Ownable --- An unshielded Ownable library. diff --git a/content/contracts-compact/utils.mdx b/content/contracts-compact/utils.mdx index 496fcd9b..0c3de4e8 100644 --- a/content/contracts-compact/utils.mdx +++ b/content/contracts-compact/utils.mdx @@ -1,6 +1,5 @@ --- title: Utils -description: Utils --- The following documentation provides context, reasoning, and examples of modules found in the Utils package. diff --git a/content/contracts-compact/zkCircuits101.mdx b/content/contracts-compact/zkCircuits101.mdx index 0ac22da4..23871a28 100644 --- a/content/contracts-compact/zkCircuits101.mdx +++ b/content/contracts-compact/zkCircuits101.mdx @@ -1,6 +1,5 @@ --- title: ZK Circuits 101 -description: ZK Circuits 101 --- ## What are ZK Circuits? diff --git a/content/contracts-stylus/access-control.mdx b/content/contracts-stylus/access-control.mdx index 56437f9b..ea2cf6d4 100644 --- a/content/contracts-stylus/access-control.mdx +++ b/content/contracts-stylus/access-control.mdx @@ -1,6 +1,5 @@ --- title: Access Control -description: Access Control --- Access control—that is, "who is allowed to do this thing"—is incredibly important in the world of smart contracts. The access control of your contract may govern who can mint tokens, vote on proposals, freeze transfers, and many other things. It is therefore **critical** to understand how you implement it, lest someone else [steals your whole system](https://blog.openzeppelin.com/on-the-parity-wallet-multisig-hack-405a8c12e8f7). diff --git a/content/contracts-stylus/beacon-proxy.mdx b/content/contracts-stylus/beacon-proxy.mdx index e014ac38..c84fe0d8 100644 --- a/content/contracts-stylus/beacon-proxy.mdx +++ b/content/contracts-stylus/beacon-proxy.mdx @@ -1,6 +1,5 @@ --- title: Beacon Proxy -description: Beacon Proxy --- Beacon Proxy is an advanced proxy pattern that allows multiple proxy contracts to share a single beacon contract that determines their implementation. This pattern is particularly useful for scenarios where you want to upgrade multiple proxy contracts simultaneously by updating a single beacon. diff --git a/content/contracts-stylus/common.mdx b/content/contracts-stylus/common.mdx index e7850356..395233ba 100644 --- a/content/contracts-stylus/common.mdx +++ b/content/contracts-stylus/common.mdx @@ -1,6 +1,5 @@ --- title: Common (Tokens) -description: Common (Tokens) --- Contracts that are common to multiple token standards. diff --git a/content/contracts-stylus/crypto.mdx b/content/contracts-stylus/crypto.mdx index 9c2b30ee..dea1625a 100644 --- a/content/contracts-stylus/crypto.mdx +++ b/content/contracts-stylus/crypto.mdx @@ -1,6 +1,5 @@ --- title: Crypto -description: Crypto --- The OpenZeppelin Rust Contracts provide a crate for common cryptographic procedures in a blockchain environment. The following documents the available functionality. diff --git a/content/contracts-stylus/erc1155-burnable.mdx b/content/contracts-stylus/erc1155-burnable.mdx index 03553aa4..b38995e2 100644 --- a/content/contracts-stylus/erc1155-burnable.mdx +++ b/content/contracts-stylus/erc1155-burnable.mdx @@ -1,6 +1,5 @@ --- title: ERC-1155 Burnable -description: ERC-1155 Burnable --- Extension of [ERC-1155](erc1155.mdx) that allows token holders to destroy both their diff --git a/content/contracts-stylus/erc1155-metadata-uri.mdx b/content/contracts-stylus/erc1155-metadata-uri.mdx index 5e129e8c..84653fcb 100644 --- a/content/contracts-stylus/erc1155-metadata-uri.mdx +++ b/content/contracts-stylus/erc1155-metadata-uri.mdx @@ -1,6 +1,5 @@ --- title: ERC-1155 Metadata URI -description: ERC-1155 Metadata URI --- The OpenZeppelin [ERC-1155](erc1155.mdx) Metadata URI extension is needed to manage and store URIs for individual tokens. This extension allows each token to have its own unique URI, diff --git a/content/contracts-stylus/erc1155-pausable.mdx b/content/contracts-stylus/erc1155-pausable.mdx index 55bf3f06..4b5fc504 100644 --- a/content/contracts-stylus/erc1155-pausable.mdx +++ b/content/contracts-stylus/erc1155-pausable.mdx @@ -1,6 +1,5 @@ --- title: ERC-1155 Pausable -description: ERC-1155 Pausable --- [ERC-1155](erc1155.mdx) token with pausable token transfers, minting, and burning. diff --git a/content/contracts-stylus/erc1155-supply.mdx b/content/contracts-stylus/erc1155-supply.mdx index 399b91cb..3757ab34 100644 --- a/content/contracts-stylus/erc1155-supply.mdx +++ b/content/contracts-stylus/erc1155-supply.mdx @@ -1,6 +1,5 @@ --- title: ERC-1155 Supply -description: ERC-1155 Supply --- The OpenZeppelin [ERC-1155](erc1155.mdx) Supply extension that adds tracking of total supply per token id. diff --git a/content/contracts-stylus/erc1155-uri-storage.mdx b/content/contracts-stylus/erc1155-uri-storage.mdx index e4f33002..ab812a9a 100644 --- a/content/contracts-stylus/erc1155-uri-storage.mdx +++ b/content/contracts-stylus/erc1155-uri-storage.mdx @@ -1,6 +1,5 @@ --- title: ERC-1155 URI Storage -description: ERC-1155 URI Storage --- The OpenZeppelin [ERC-1155](erc1155.mdx) URI Storage extension is needed to manage and store URIs for individual tokens. This extension allows each token to have its own unique URI, diff --git a/content/contracts-stylus/erc1155.mdx b/content/contracts-stylus/erc1155.mdx index b504679c..ac13d3f8 100644 --- a/content/contracts-stylus/erc1155.mdx +++ b/content/contracts-stylus/erc1155.mdx @@ -1,6 +1,5 @@ --- title: ERC-1155 -description: ERC-1155 --- ERC1155 is a novel token standard that aims to take the best from previous standards to create a [**fungibility-agnostic**](tokens.mdx#different-kinds-of-tokens) and **gas-efficient** [token contract](tokens.mdx#but_first_coffee_a_primer_on_token_contracts). diff --git a/content/contracts-stylus/erc1967.mdx b/content/contracts-stylus/erc1967.mdx index 52a0dfcb..2c3dde44 100644 --- a/content/contracts-stylus/erc1967.mdx +++ b/content/contracts-stylus/erc1967.mdx @@ -1,6 +1,5 @@ --- title: ERC-1967 Proxy -description: ERC-1967 Proxy --- ERC-1967 is a standardized proxy pattern that defines specific storage slots for proxy contracts to prevent storage collisions between the proxy and implementation contracts. This standard ensures that proxy contracts can be safely upgraded without conflicts. diff --git a/content/contracts-stylus/erc20-burnable.mdx b/content/contracts-stylus/erc20-burnable.mdx index 296b5521..e3fcad40 100644 --- a/content/contracts-stylus/erc20-burnable.mdx +++ b/content/contracts-stylus/erc20-burnable.mdx @@ -1,6 +1,5 @@ --- title: ERC-20 Burnable -description: ERC-20 Burnable --- Extension of [ERC-20](erc20.mdx) that allows token holders to destroy both their own tokens and those that they have an allowance for, in a way that can be recognized off-chain (via event analysis). diff --git a/content/contracts-stylus/erc20-capped.mdx b/content/contracts-stylus/erc20-capped.mdx index 2a8a23d9..a552d100 100644 --- a/content/contracts-stylus/erc20-capped.mdx +++ b/content/contracts-stylus/erc20-capped.mdx @@ -1,6 +1,5 @@ --- title: ERC-20 Capped -description: ERC-20 Capped --- Extension of [ERC-20](erc20.mdx) that adds a cap to the supply of tokens. diff --git a/content/contracts-stylus/erc20-flash-mint.mdx b/content/contracts-stylus/erc20-flash-mint.mdx index 433c79cb..460bc386 100644 --- a/content/contracts-stylus/erc20-flash-mint.mdx +++ b/content/contracts-stylus/erc20-flash-mint.mdx @@ -1,6 +1,5 @@ --- title: ERC-20 Flash Mint -description: ERC-20 Flash Mint --- Extension of [ERC-20](erc20.mdx) that provides flash loan support at the token level. diff --git a/content/contracts-stylus/erc20-metadata.mdx b/content/contracts-stylus/erc20-metadata.mdx index 0d83ff7d..3fc6cfeb 100644 --- a/content/contracts-stylus/erc20-metadata.mdx +++ b/content/contracts-stylus/erc20-metadata.mdx @@ -1,6 +1,5 @@ --- title: ERC-20 Metadata -description: ERC-20 Metadata --- Extension of [ERC-20](erc20.mdx) that adds the optional metadata functions from the ERC20 standard. diff --git a/content/contracts-stylus/erc20-pausable.mdx b/content/contracts-stylus/erc20-pausable.mdx index aeca3ec5..50d6cd96 100644 --- a/content/contracts-stylus/erc20-pausable.mdx +++ b/content/contracts-stylus/erc20-pausable.mdx @@ -1,6 +1,5 @@ --- title: ERC-20 Pausable -description: ERC-20 Pausable --- ERC20 token with pausable token transfers, minting, and burning. diff --git a/content/contracts-stylus/erc20-permit.mdx b/content/contracts-stylus/erc20-permit.mdx index aaa4b107..58728ddd 100644 --- a/content/contracts-stylus/erc20-permit.mdx +++ b/content/contracts-stylus/erc20-permit.mdx @@ -1,6 +1,5 @@ --- title: ERC-20 Permit -description: ERC-20 Permit --- Adds the permit method, which can be used to change an account’s ERC20 allowance (see [`IErc20::allowance`](https://docs.rs/openzeppelin-stylus/0.3.0-alpha.1/openzeppelin_stylus/token/erc20/trait.IErc20.html#tymethod.allowance)) by presenting a message signed by the account. By not relying on [`IErc20::approve`](https://docs.rs/openzeppelin-stylus/0.3.0-alpha.1/openzeppelin_stylus/token/erc20/trait.IErc20.html#tymethod.approve), the token holder account doesn’t need to send a transaction, and thus is not required to hold Ether at all. diff --git a/content/contracts-stylus/erc20-wrapper.mdx b/content/contracts-stylus/erc20-wrapper.mdx index 2e0e4530..c410bd06 100644 --- a/content/contracts-stylus/erc20-wrapper.mdx +++ b/content/contracts-stylus/erc20-wrapper.mdx @@ -1,6 +1,5 @@ --- title: ERC-20 Wrapper -description: ERC-20 Wrapper --- Extension of the ERC-20 token contract to support token wrapping. diff --git a/content/contracts-stylus/erc20.mdx b/content/contracts-stylus/erc20.mdx index 9e73aeec..0ba700f9 100644 --- a/content/contracts-stylus/erc20.mdx +++ b/content/contracts-stylus/erc20.mdx @@ -1,6 +1,5 @@ --- title: ERC-20 -description: ERC-20 --- An ERC-20 token contract keeps track of [_fungible_ tokens](tokens.mdx#different-kinds-of-tokens): any token is exactly equal to any other token; no token has a special right or behavior associated with them. diff --git a/content/contracts-stylus/erc2981.mdx b/content/contracts-stylus/erc2981.mdx index e13abc5a..3b968696 100644 --- a/content/contracts-stylus/erc2981.mdx +++ b/content/contracts-stylus/erc2981.mdx @@ -1,6 +1,5 @@ --- title: ERC-2981 -description: ERC-2981 --- ERC-2981 offers a way to keep track of NFT royalties across different platforms. diff --git a/content/contracts-stylus/erc4626.mdx b/content/contracts-stylus/erc4626.mdx index f04696ac..56b1ab91 100644 --- a/content/contracts-stylus/erc4626.mdx +++ b/content/contracts-stylus/erc4626.mdx @@ -1,6 +1,5 @@ --- title: ERC-4626 -description: ERC-4626 --- Implementation of the ERC-4626 "Tokenized Vault Standard" as defined in [ERC-4626](https://eips.ethereum.org/EIPS/eip-4626). diff --git a/content/contracts-stylus/erc721-burnable.mdx b/content/contracts-stylus/erc721-burnable.mdx index c5f2c4be..b939b16c 100644 --- a/content/contracts-stylus/erc721-burnable.mdx +++ b/content/contracts-stylus/erc721-burnable.mdx @@ -1,6 +1,5 @@ --- title: ERC-721 Burnable -description: ERC-721 Burnable --- [ERC-721](erc721.mdx) Token that can be burned (destroyed). diff --git a/content/contracts-stylus/erc721-consecutive.mdx b/content/contracts-stylus/erc721-consecutive.mdx index eda03f07..f57ccbde 100644 --- a/content/contracts-stylus/erc721-consecutive.mdx +++ b/content/contracts-stylus/erc721-consecutive.mdx @@ -1,6 +1,5 @@ --- title: ERC-721 Consecutive -description: ERC-721 Consecutive --- Consecutive extension for [ERC-721](erc721.mdx) is useful for efficiently minting multiple tokens in a single transaction. This can significantly reduce gas costs and improve performance when creating a large number of tokens at once. diff --git a/content/contracts-stylus/erc721-enumerable.mdx b/content/contracts-stylus/erc721-enumerable.mdx index 872d9c36..97b85ead 100644 --- a/content/contracts-stylus/erc721-enumerable.mdx +++ b/content/contracts-stylus/erc721-enumerable.mdx @@ -1,6 +1,5 @@ --- title: ERC-721 Enumerable -description: ERC-721 Enumerable --- The OpenZeppelin [ERC-721](erc721.mdx) Enumerable extension is used to provide additional functionality to the standard ERC-721 token. Specifically, it allows for enumeration of all the token IDs in the contract as well as all the token IDs owned by each account. This is useful for applications that need to list or iterate over tokens, such as marketplaces or wallets. diff --git a/content/contracts-stylus/erc721-metadata.mdx b/content/contracts-stylus/erc721-metadata.mdx index 11dcfdd5..ea4ae9d2 100644 --- a/content/contracts-stylus/erc721-metadata.mdx +++ b/content/contracts-stylus/erc721-metadata.mdx @@ -1,6 +1,5 @@ --- title: ERC-721 Metadata -description: ERC-721 Metadata --- Extension of [ERC-721](erc721.mdx) that adds the optional metadata functions from the ERC721 standard. diff --git a/content/contracts-stylus/erc721-pausable.mdx b/content/contracts-stylus/erc721-pausable.mdx index 5998c5db..087b57ff 100644 --- a/content/contracts-stylus/erc721-pausable.mdx +++ b/content/contracts-stylus/erc721-pausable.mdx @@ -1,6 +1,5 @@ --- title: ERC-721 Pausable -description: ERC-721 Pausable --- ERC721 token with pausable token transfers, minting, and burning. diff --git a/content/contracts-stylus/erc721-uri-storage.mdx b/content/contracts-stylus/erc721-uri-storage.mdx index 75988855..2115893d 100644 --- a/content/contracts-stylus/erc721-uri-storage.mdx +++ b/content/contracts-stylus/erc721-uri-storage.mdx @@ -1,6 +1,5 @@ --- title: ERC-721 Uri Storage -description: ERC-721 Uri Storage --- The OpenZeppelin [ERC-721](erc721.mdx) URI Storage extension is needed to manage and store URIs for individual tokens. This extension allows each token to have its own unique URI, diff --git a/content/contracts-stylus/erc721-wrapper.mdx b/content/contracts-stylus/erc721-wrapper.mdx index e5f5619f..c4e07cde 100644 --- a/content/contracts-stylus/erc721-wrapper.mdx +++ b/content/contracts-stylus/erc721-wrapper.mdx @@ -1,6 +1,5 @@ --- title: ERC-721 Wrapper -description: ERC-721 Wrapper --- Extension of the ERC-721 token contract to support token wrapping. diff --git a/content/contracts-stylus/erc721.mdx b/content/contracts-stylus/erc721.mdx index f9cccd5d..750c42e1 100644 --- a/content/contracts-stylus/erc721.mdx +++ b/content/contracts-stylus/erc721.mdx @@ -1,6 +1,5 @@ --- title: ERC-721 -description: ERC-721 --- We’ve discussed how you can make a _fungible_ token using [ERC-20](erc20.mdx), but what if not all tokens are alike? diff --git a/content/contracts-stylus/finance.mdx b/content/contracts-stylus/finance.mdx index ab2f650d..161bb179 100644 --- a/content/contracts-stylus/finance.mdx +++ b/content/contracts-stylus/finance.mdx @@ -1,6 +1,5 @@ --- title: Finance -description: Finance --- Contracts that include primitives for financial systems. diff --git a/content/contracts-stylus/index.mdx b/content/contracts-stylus/index.mdx index 8c19f2cc..db831158 100644 --- a/content/contracts-stylus/index.mdx +++ b/content/contracts-stylus/index.mdx @@ -1,6 +1,5 @@ --- title: OpenZeppelin Contracts for Stylus -description: OpenZeppelin Contracts for Stylus --- **A secure, modular smart contract library for [Stylus](https://docs.arbitrum.io/stylus/gentle-introduction), written in Rust.** diff --git a/content/contracts-stylus/proxy.mdx b/content/contracts-stylus/proxy.mdx index f2d1551a..cc9497fa 100644 --- a/content/contracts-stylus/proxy.mdx +++ b/content/contracts-stylus/proxy.mdx @@ -1,6 +1,5 @@ --- title: Proxy Patterns -description: Proxy Patterns --- Proxy contracts are a fundamental pattern in smart contract development that allow you to separate the storage and logic of your contracts. This enables powerful features like upgradeability, gas optimization, and code reuse. diff --git a/content/contracts-stylus/tokens.mdx b/content/contracts-stylus/tokens.mdx index 17f77a9b..930a1602 100644 --- a/content/contracts-stylus/tokens.mdx +++ b/content/contracts-stylus/tokens.mdx @@ -1,6 +1,5 @@ --- title: Tokens -description: Tokens --- Ah, the "token": blockchain’s most powerful and most misunderstood tool. diff --git a/content/contracts-stylus/utilities.mdx b/content/contracts-stylus/utilities.mdx index 14bc8144..40bd92e5 100644 --- a/content/contracts-stylus/utilities.mdx +++ b/content/contracts-stylus/utilities.mdx @@ -1,6 +1,5 @@ --- title: Utilities -description: Utilities --- The OpenZeppelin Stylus Contracts provides a ton of useful utilities that you can use in your project. diff --git a/content/contracts-stylus/uups-proxy.mdx b/content/contracts-stylus/uups-proxy.mdx index 3389789b..42d29231 100644 --- a/content/contracts-stylus/uups-proxy.mdx +++ b/content/contracts-stylus/uups-proxy.mdx @@ -1,6 +1,5 @@ --- title: UUPS Proxy -description: UUPS Proxy --- The Universal Upgradeable Proxy Standard (UUPS) is a minimal and gas-efficient diff --git a/content/contracts-stylus/vesting-wallet.mdx b/content/contracts-stylus/vesting-wallet.mdx index ef3a95dd..aff2b797 100644 --- a/content/contracts-stylus/vesting-wallet.mdx +++ b/content/contracts-stylus/vesting-wallet.mdx @@ -1,6 +1,5 @@ --- title: VestingWallet -description: VestingWallet --- A vesting wallet is an ownable contract that can receive native currency and diff --git a/content/contracts-ui-builder/building-adapters.mdx b/content/contracts-ui-builder/building-adapters.mdx index 6d634662..457c3028 100644 --- a/content/contracts-ui-builder/building-adapters.mdx +++ b/content/contracts-ui-builder/building-adapters.mdx @@ -1,6 +1,5 @@ --- title: Building New Adapters -description: Building New Adapters --- This document provides a comprehensive overview of the Contracts UI Builder’s architecture, with a focus on the patterns and requirements for creating new ecosystem adapters. It’s intended for developers tasked with extending the platform to support new blockchains. diff --git a/content/contracts-ui-builder/customization.mdx b/content/contracts-ui-builder/customization.mdx index f2669421..116b5c28 100644 --- a/content/contracts-ui-builder/customization.mdx +++ b/content/contracts-ui-builder/customization.mdx @@ -1,6 +1,5 @@ --- title: Customization -description: Customization --- Once you have selected the function you want to build a form for, you can finally customize the experience the form will take. diff --git a/content/contracts-ui-builder/exporting-and-history.mdx b/content/contracts-ui-builder/exporting-and-history.mdx index 55be9218..0657873e 100644 --- a/content/contracts-ui-builder/exporting-and-history.mdx +++ b/content/contracts-ui-builder/exporting-and-history.mdx @@ -1,6 +1,5 @@ --- title: Exporting and History -description: Exporting and History --- Once you have completed customizing your form it’s ready to ship! There are a few ways you can export and save previous UIs. diff --git a/content/contracts-ui-builder/functions.mdx b/content/contracts-ui-builder/functions.mdx index 22515dd7..c90be533 100644 --- a/content/contracts-ui-builder/functions.mdx +++ b/content/contracts-ui-builder/functions.mdx @@ -1,6 +1,5 @@ --- title: Functions -description: Functions --- Once you have provided your contract information you can choose which function you would like to build a form for. At the moment forms are limited to a single function. Since contract state is included by default only write functions are available for forms. diff --git a/content/contracts-ui-builder/index.mdx b/content/contracts-ui-builder/index.mdx index 2692d808..91104200 100644 --- a/content/contracts-ui-builder/index.mdx +++ b/content/contracts-ui-builder/index.mdx @@ -1,6 +1,5 @@ --- title: Quickstart -description: Quickstart --- The Contracts UI Builder is an open source tool you can quickly create online forms to interact with your smart contracts for testing or for administration purposes. It includes a vast amount of features including: diff --git a/content/contracts-ui-builder/loading-contracts.mdx b/content/contracts-ui-builder/loading-contracts.mdx index 3c394b25..cc775636 100644 --- a/content/contracts-ui-builder/loading-contracts.mdx +++ b/content/contracts-ui-builder/loading-contracts.mdx @@ -1,6 +1,5 @@ --- title: Loading Contracts -description: Loading Contracts --- After you have selected your chain you can paste in the deployed contract address. After providing the contract address the UI Builder will try to use public block explorer APIs to fetch the contract ABIs. This will only work if the contract is verified. diff --git a/content/contracts-ui-builder/networks.mdx b/content/contracts-ui-builder/networks.mdx index ec2e3039..5a3734ce 100644 --- a/content/contracts-ui-builder/networks.mdx +++ b/content/contracts-ui-builder/networks.mdx @@ -1,6 +1,5 @@ --- title: Networks -description: Networks --- ## Supported Networks diff --git a/content/contracts/v2.x/access-control.mdx b/content/contracts/v2.x/access-control.mdx index 52f382bf..cc51ec6d 100644 --- a/content/contracts/v2.x/access-control.mdx +++ b/content/contracts/v2.x/access-control.mdx @@ -1,6 +1,5 @@ --- title: Access Control -description: Access Control --- Access control—that is, "who is allowed to do this thing"—is incredibly important in the world of smart contracts. The access control of your contract may govern who can mint tokens, vote on proposals, freeze transfers, and many other things. It is therefore **critical** to understand how you implement it, lest someone else [steals your whole system](https://blog.openzeppelin.com/on-the-parity-wallet-multisig-hack-405a8c12e8f7). diff --git a/content/contracts/v2.x/api/GSN.mdx b/content/contracts/v2.x/api/GSN.mdx index 2ab3f2b9..0d7678ad 100644 --- a/content/contracts/v2.x/api/GSN.mdx +++ b/content/contracts/v2.x/api/GSN.mdx @@ -1,6 +1,5 @@ --- title: Gas Station Network (GSN) -description: Gas Station Network (GSN) --- diff --git a/content/contracts/v2.x/api/access.mdx b/content/contracts/v2.x/api/access.mdx index 85ed93e9..320d8cf8 100644 --- a/content/contracts/v2.x/api/access.mdx +++ b/content/contracts/v2.x/api/access.mdx @@ -1,6 +1,5 @@ --- title: Access -description: Access --- diff --git a/content/contracts/v2.x/api/cryptography.mdx b/content/contracts/v2.x/api/cryptography.mdx index bc4ebddb..1f0d4f98 100644 --- a/content/contracts/v2.x/api/cryptography.mdx +++ b/content/contracts/v2.x/api/cryptography.mdx @@ -1,6 +1,5 @@ --- title: Cryptography -description: Cryptography --- diff --git a/content/contracts/v2.x/api/drafts.mdx b/content/contracts/v2.x/api/drafts.mdx index e8b1b26e..21f216ad 100644 --- a/content/contracts/v2.x/api/drafts.mdx +++ b/content/contracts/v2.x/api/drafts.mdx @@ -1,6 +1,5 @@ --- title: Draft EIPs -description: Draft EIPs --- This directory contains implementations of EIPs that are still in Draft status. diff --git a/content/contracts/v2.x/api/introspection.mdx b/content/contracts/v2.x/api/introspection.mdx index 33ef4e5b..4268e349 100644 --- a/content/contracts/v2.x/api/introspection.mdx +++ b/content/contracts/v2.x/api/introspection.mdx @@ -1,6 +1,5 @@ --- title: Introspection -description: Introspection --- diff --git a/content/contracts/v2.x/api/math.mdx b/content/contracts/v2.x/api/math.mdx index e9ca154b..2626b493 100644 --- a/content/contracts/v2.x/api/math.mdx +++ b/content/contracts/v2.x/api/math.mdx @@ -1,6 +1,5 @@ --- title: Math -description: Math --- diff --git a/content/contracts/v2.x/api/payment.mdx b/content/contracts/v2.x/api/payment.mdx index 66acd742..9c5df95a 100644 --- a/content/contracts/v2.x/api/payment.mdx +++ b/content/contracts/v2.x/api/payment.mdx @@ -1,6 +1,5 @@ --- title: Payment -description: Payment --- diff --git a/content/contracts/v2.x/api/presets.mdx b/content/contracts/v2.x/api/presets.mdx index 119d6b0a..eeb08a88 100644 --- a/content/contracts/v2.x/api/presets.mdx +++ b/content/contracts/v2.x/api/presets.mdx @@ -1,6 +1,5 @@ --- title: Presets -description: Presets --- diff --git a/content/contracts/v2.x/api/proxy.mdx b/content/contracts/v2.x/api/proxy.mdx index 348d1324..dee9e873 100644 --- a/content/contracts/v2.x/api/proxy.mdx +++ b/content/contracts/v2.x/api/proxy.mdx @@ -1,6 +1,5 @@ --- title: Proxies -description: Proxies --- diff --git a/content/contracts/v2.x/api/token/ERC1155.mdx b/content/contracts/v2.x/api/token/ERC1155.mdx index 37e1af03..f8386db4 100644 --- a/content/contracts/v2.x/api/token/ERC1155.mdx +++ b/content/contracts/v2.x/api/token/ERC1155.mdx @@ -1,6 +1,5 @@ --- title: ERC 1155 -description: ERC 1155 --- diff --git a/content/contracts/v2.x/api/token/ERC20.mdx b/content/contracts/v2.x/api/token/ERC20.mdx index 540d5192..76262edd 100644 --- a/content/contracts/v2.x/api/token/ERC20.mdx +++ b/content/contracts/v2.x/api/token/ERC20.mdx @@ -1,6 +1,5 @@ --- title: ERC 20 -description: ERC 20 --- diff --git a/content/contracts/v2.x/api/token/ERC721.mdx b/content/contracts/v2.x/api/token/ERC721.mdx index b6f7994e..ef2587c4 100644 --- a/content/contracts/v2.x/api/token/ERC721.mdx +++ b/content/contracts/v2.x/api/token/ERC721.mdx @@ -1,6 +1,5 @@ --- title: ERC 721 -description: ERC 721 --- diff --git a/content/contracts/v2.x/api/token/ERC777.mdx b/content/contracts/v2.x/api/token/ERC777.mdx index 1217ec90..25833af6 100644 --- a/content/contracts/v2.x/api/token/ERC777.mdx +++ b/content/contracts/v2.x/api/token/ERC777.mdx @@ -1,6 +1,5 @@ --- title: ERC 777 -description: ERC 777 --- diff --git a/content/contracts/v2.x/api/utils.mdx b/content/contracts/v2.x/api/utils.mdx index 39bf22de..c931c7d4 100644 --- a/content/contracts/v2.x/api/utils.mdx +++ b/content/contracts/v2.x/api/utils.mdx @@ -1,6 +1,5 @@ --- title: Utilities -description: Utilities --- diff --git a/content/contracts/v2.x/crowdsales.mdx b/content/contracts/v2.x/crowdsales.mdx index b53e8d58..f581d97d 100644 --- a/content/contracts/v2.x/crowdsales.mdx +++ b/content/contracts/v2.x/crowdsales.mdx @@ -1,6 +1,5 @@ --- title: Crowdsales -description: Crowdsales --- Crowdsales are a popular use for Ethereum; they let you allocate tokens to network participants in various ways, mostly in exchange for Ether. They come in a variety of shapes and flavors, so let’s go over the various types available in OpenZeppelin Contracts and how to use them. diff --git a/content/contracts/v2.x/erc20-supply.mdx b/content/contracts/v2.x/erc20-supply.mdx index cfabb1f2..52a062dc 100644 --- a/content/contracts/v2.x/erc20-supply.mdx +++ b/content/contracts/v2.x/erc20-supply.mdx @@ -1,6 +1,5 @@ --- title: Creating ERC20 Supply -description: Creating ERC20 Supply --- In this guide you will learn how to create an ERC20 token with a custom supply mechanism. We will showcase two idiomatic ways to use OpenZeppelin Contracts for this purpose that you will be able to apply to your smart contract development practice. diff --git a/content/contracts/v2.x/erc20.mdx b/content/contracts/v2.x/erc20.mdx index ec8eb97c..83c3b905 100644 --- a/content/contracts/v2.x/erc20.mdx +++ b/content/contracts/v2.x/erc20.mdx @@ -1,6 +1,5 @@ --- title: ERC20 -description: ERC20 --- An ERC20 token contract keeps track of [_fungible_ tokens](tokens.mdx#different-kinds-of-tokens): any one token is exactly equal to any other token; no tokens have special rights or behavior associated with them. This makes ERC20 tokens useful for things like a **medium of exchange currency**, **voting rights**, **staking**, and more. diff --git a/content/contracts/v2.x/erc721.mdx b/content/contracts/v2.x/erc721.mdx index 4da8a023..6e9ff29b 100644 --- a/content/contracts/v2.x/erc721.mdx +++ b/content/contracts/v2.x/erc721.mdx @@ -1,6 +1,5 @@ --- title: ERC721 -description: ERC721 --- We’ve discussed how you can make a _fungible_ token using [ERC20](erc20.mdx), but what if not all tokens are alike? This comes up in situations like **real estate** or **collectibles**, where some items are valued more than others, due to their usefulness, rarity, etc. ERC721 is a standard for representing ownership of [_non-fungible_ tokens](tokens.mdx#different-kinds-of-tokens), that is, where each token is unique. diff --git a/content/contracts/v2.x/erc777.mdx b/content/contracts/v2.x/erc777.mdx index 84e2914c..3f776f7a 100644 --- a/content/contracts/v2.x/erc777.mdx +++ b/content/contracts/v2.x/erc777.mdx @@ -1,6 +1,5 @@ --- title: ERC777 -description: ERC777 --- Like [ERC20](erc20.mdx), ERC777 is a standard for [_fungible_ tokens](tokens.mdx#different-kinds-of-tokens), and is focused around allowing more complex interactions when trading tokens. More generally, it brings tokens and Ether closer together by providing the equivalent of a `msg.value` field, but for tokens. diff --git a/content/contracts/v2.x/gsn-strategies.mdx b/content/contracts/v2.x/gsn-strategies.mdx index ee20c612..e059ad72 100644 --- a/content/contracts/v2.x/gsn-strategies.mdx +++ b/content/contracts/v2.x/gsn-strategies.mdx @@ -1,6 +1,5 @@ --- title: GSN Strategies -description: GSN Strategies --- This guide shows you different strategies to accept relayed calls via the Gas Station Network (GSN) using OpenZeppelin Contracts. diff --git a/content/contracts/v2.x/gsn.mdx b/content/contracts/v2.x/gsn.mdx index 733a7023..d3fa54e5 100644 --- a/content/contracts/v2.x/gsn.mdx +++ b/content/contracts/v2.x/gsn.mdx @@ -1,6 +1,5 @@ --- title: Writing GSN-capable contracts -description: Writing GSN-capable contracts --- The [Gas Station Network](https://gsn.openzeppelin.com) allows you to build apps where you pay for your users transactions, so they do not need to hold Ether to pay for gas, easing their onboarding process. In this guide, we will learn how to write smart contracts that can receive transactions from the GSN, by using OpenZeppelin Contracts. diff --git a/content/contracts/v2.x/index.mdx b/content/contracts/v2.x/index.mdx index 5917e987..a07e9f1d 100644 --- a/content/contracts/v2.x/index.mdx +++ b/content/contracts/v2.x/index.mdx @@ -1,6 +1,5 @@ --- title: Contracts -description: Contracts --- **A library for secure smart contract development.** Build on a solid foundation of community-vetted code. diff --git a/content/contracts/v2.x/releases-stability.mdx b/content/contracts/v2.x/releases-stability.mdx index cb4b744d..7f77353a 100644 --- a/content/contracts/v2.x/releases-stability.mdx +++ b/content/contracts/v2.x/releases-stability.mdx @@ -1,6 +1,5 @@ --- title: New Releases and API Stability -description: New Releases and API Stability --- Developing smart contracts is hard, and a conservative approach towards dependencies is sometimes favored. However, it is also very important to stay on top of new releases: these may include bugfixes, or deprecate old patterns in favor of newer and better practices. diff --git a/content/contracts/v2.x/tokens.mdx b/content/contracts/v2.x/tokens.mdx index a545a5ec..06a4f1be 100644 --- a/content/contracts/v2.x/tokens.mdx +++ b/content/contracts/v2.x/tokens.mdx @@ -1,6 +1,5 @@ --- title: Tokens -description: Tokens --- Ah, the "token": blockchain’s most powerful and most misunderstood tool. diff --git a/content/contracts/v2.x/utilities.mdx b/content/contracts/v2.x/utilities.mdx index e01ddc4f..ee6fc20b 100644 --- a/content/contracts/v2.x/utilities.mdx +++ b/content/contracts/v2.x/utilities.mdx @@ -1,6 +1,5 @@ --- title: Utilities -description: Utilities --- The OpenZeppelin Contracs provide a ton of useful utilities that you can use in your project. Here are some of the more popular ones. diff --git a/content/contracts/v3.x/access-control.mdx b/content/contracts/v3.x/access-control.mdx index f0ec2a15..8aebd965 100644 --- a/content/contracts/v3.x/access-control.mdx +++ b/content/contracts/v3.x/access-control.mdx @@ -1,6 +1,5 @@ --- title: Access Control -description: Access Control --- Access control—that is, "who is allowed to do this thing"—is incredibly important in the world of smart contracts. The access control of your contract may govern who can mint tokens, vote on proposals, freeze transfers, and many other things. It is therefore **critical** to understand how you implement it, lest someone else [steals your whole system](https://blog.openzeppelin.com/on-the-parity-wallet-multisig-hack-405a8c12e8f7). diff --git a/content/contracts/v3.x/api/GSN.mdx b/content/contracts/v3.x/api/GSN.mdx index dd6a418d..2e727ce4 100644 --- a/content/contracts/v3.x/api/GSN.mdx +++ b/content/contracts/v3.x/api/GSN.mdx @@ -1,6 +1,5 @@ --- title: Gas Station Network (GSN) -description: Gas Station Network (GSN) --- diff --git a/content/contracts/v3.x/api/access.mdx b/content/contracts/v3.x/api/access.mdx index 346093d5..ac849d37 100644 --- a/content/contracts/v3.x/api/access.mdx +++ b/content/contracts/v3.x/api/access.mdx @@ -1,6 +1,5 @@ --- title: Access -description: Access --- diff --git a/content/contracts/v3.x/api/cryptography.mdx b/content/contracts/v3.x/api/cryptography.mdx index 8b1c371e..697d0636 100644 --- a/content/contracts/v3.x/api/cryptography.mdx +++ b/content/contracts/v3.x/api/cryptography.mdx @@ -1,6 +1,5 @@ --- title: Cryptography -description: Cryptography --- diff --git a/content/contracts/v3.x/api/drafts.mdx b/content/contracts/v3.x/api/drafts.mdx index e3812407..fbafc464 100644 --- a/content/contracts/v3.x/api/drafts.mdx +++ b/content/contracts/v3.x/api/drafts.mdx @@ -1,6 +1,5 @@ --- title: Draft EIPs -description: Draft EIPs --- This directory contains implementations of EIPs that are still in Draft status. diff --git a/content/contracts/v3.x/api/introspection.mdx b/content/contracts/v3.x/api/introspection.mdx index 5faebe93..1b297343 100644 --- a/content/contracts/v3.x/api/introspection.mdx +++ b/content/contracts/v3.x/api/introspection.mdx @@ -1,6 +1,5 @@ --- title: Introspection -description: Introspection --- diff --git a/content/contracts/v3.x/api/math.mdx b/content/contracts/v3.x/api/math.mdx index 49d61fa4..88fc46a9 100644 --- a/content/contracts/v3.x/api/math.mdx +++ b/content/contracts/v3.x/api/math.mdx @@ -1,6 +1,5 @@ --- title: Math -description: Math --- diff --git a/content/contracts/v3.x/api/payment.mdx b/content/contracts/v3.x/api/payment.mdx index d9e6e2d0..e3a573c6 100644 --- a/content/contracts/v3.x/api/payment.mdx +++ b/content/contracts/v3.x/api/payment.mdx @@ -1,6 +1,5 @@ --- title: Payment -description: Payment --- diff --git a/content/contracts/v3.x/api/presets.mdx b/content/contracts/v3.x/api/presets.mdx index 7913a8a4..3d538858 100644 --- a/content/contracts/v3.x/api/presets.mdx +++ b/content/contracts/v3.x/api/presets.mdx @@ -1,6 +1,5 @@ --- title: Presets -description: Presets --- diff --git a/content/contracts/v3.x/api/proxy.mdx b/content/contracts/v3.x/api/proxy.mdx index 5f1515df..368e7a66 100644 --- a/content/contracts/v3.x/api/proxy.mdx +++ b/content/contracts/v3.x/api/proxy.mdx @@ -1,6 +1,5 @@ --- title: Proxies -description: Proxies --- diff --git a/content/contracts/v3.x/api/token/ERC1155.mdx b/content/contracts/v3.x/api/token/ERC1155.mdx index 10796b8b..5d2a8ece 100644 --- a/content/contracts/v3.x/api/token/ERC1155.mdx +++ b/content/contracts/v3.x/api/token/ERC1155.mdx @@ -1,6 +1,5 @@ --- title: ERC 1155 -description: ERC 1155 --- diff --git a/content/contracts/v3.x/api/token/ERC20.mdx b/content/contracts/v3.x/api/token/ERC20.mdx index 5075682f..14afe567 100644 --- a/content/contracts/v3.x/api/token/ERC20.mdx +++ b/content/contracts/v3.x/api/token/ERC20.mdx @@ -1,6 +1,5 @@ --- title: ERC 20 -description: ERC 20 --- diff --git a/content/contracts/v3.x/api/token/ERC721.mdx b/content/contracts/v3.x/api/token/ERC721.mdx index 630ce126..c47b1fcc 100644 --- a/content/contracts/v3.x/api/token/ERC721.mdx +++ b/content/contracts/v3.x/api/token/ERC721.mdx @@ -1,6 +1,5 @@ --- title: ERC 721 -description: ERC 721 --- diff --git a/content/contracts/v3.x/api/token/ERC777.mdx b/content/contracts/v3.x/api/token/ERC777.mdx index 140d8a60..316edb81 100644 --- a/content/contracts/v3.x/api/token/ERC777.mdx +++ b/content/contracts/v3.x/api/token/ERC777.mdx @@ -1,6 +1,5 @@ --- title: ERC 777 -description: ERC 777 --- diff --git a/content/contracts/v3.x/api/utils.mdx b/content/contracts/v3.x/api/utils.mdx index ca68c41a..0e57fb3a 100644 --- a/content/contracts/v3.x/api/utils.mdx +++ b/content/contracts/v3.x/api/utils.mdx @@ -1,6 +1,5 @@ --- title: Utilities -description: Utilities --- diff --git a/content/contracts/v3.x/crowdsales.mdx b/content/contracts/v3.x/crowdsales.mdx index ac803af9..9240a0b2 100644 --- a/content/contracts/v3.x/crowdsales.mdx +++ b/content/contracts/v3.x/crowdsales.mdx @@ -1,6 +1,5 @@ --- title: Crowdsales -description: Crowdsales --- All crowdsale-related contracts were removed from the OpenZeppelin Contracts library on the [v3.0.0 release](https://forum.openzeppelin.com/t/openzeppelin-contracts-v3-0-beta-release/2256) due to both a decline in their usage and the complexity associated with migrating them to Solidity v0.6. diff --git a/content/contracts/v3.x/drafts.mdx b/content/contracts/v3.x/drafts.mdx index 06d55bda..94fb2c55 100644 --- a/content/contracts/v3.x/drafts.mdx +++ b/content/contracts/v3.x/drafts.mdx @@ -1,6 +1,5 @@ --- title: Drafts -description: Drafts --- All draft contracts were either moved into a different directory or removed from the OpenZeppelin Contracts library on the [v3.0.0 release](https://forum.openzeppelin.com/t/openzeppelin-contracts-v3-0-beta-release/2256). diff --git a/content/contracts/v3.x/erc1155.mdx b/content/contracts/v3.x/erc1155.mdx index bf3b3a76..65a70de9 100644 --- a/content/contracts/v3.x/erc1155.mdx +++ b/content/contracts/v3.x/erc1155.mdx @@ -1,6 +1,5 @@ --- title: ERC1155 -description: ERC1155 --- ERC1155 is a novel token standard that aims to take the best from previous standards to create a [**fungibility-agnostic**](tokens.mdx#different-kinds-of-tokens) and **gas-efficient** [token contract](tokens.mdx#but_first_coffee_a_primer_on_token_contracts). diff --git a/content/contracts/v3.x/erc20-supply.mdx b/content/contracts/v3.x/erc20-supply.mdx index fb7bf7fa..1e0e5aaa 100644 --- a/content/contracts/v3.x/erc20-supply.mdx +++ b/content/contracts/v3.x/erc20-supply.mdx @@ -1,6 +1,5 @@ --- title: Creating ERC20 Supply -description: Creating ERC20 Supply --- In this guide you will learn how to create an ERC20 token with a custom supply mechanism. We will showcase two idiomatic ways to use OpenZeppelin Contracts for this purpose that you will be able to apply to your smart contract development practice. diff --git a/content/contracts/v3.x/erc20.mdx b/content/contracts/v3.x/erc20.mdx index 874fe2b0..80f5d42d 100644 --- a/content/contracts/v3.x/erc20.mdx +++ b/content/contracts/v3.x/erc20.mdx @@ -1,6 +1,5 @@ --- title: ERC20 -description: ERC20 --- An ERC20 token contract keeps track of [_fungible_ tokens](tokens.mdx#different-kinds-of-tokens): any one token is exactly equal to any other token; no tokens have special rights or behavior associated with them. This makes ERC20 tokens useful for things like a **medium of exchange currency**, **voting rights**, **staking**, and more. diff --git a/content/contracts/v3.x/erc721.mdx b/content/contracts/v3.x/erc721.mdx index 7fb1ec3f..1bf26f98 100644 --- a/content/contracts/v3.x/erc721.mdx +++ b/content/contracts/v3.x/erc721.mdx @@ -1,6 +1,5 @@ --- title: ERC721 -description: ERC721 --- We’ve discussed how you can make a _fungible_ token using [ERC20](erc20.mdx), but what if not all tokens are alike? This comes up in situations like **real estate** or **collectibles**, where some items are valued more than others, due to their usefulness, rarity, etc. ERC721 is a standard for representing ownership of [_non-fungible_ tokens](tokens.mdx#different-kinds-of-tokens), that is, where each token is unique. diff --git a/content/contracts/v3.x/erc777.mdx b/content/contracts/v3.x/erc777.mdx index 26822421..442d7a78 100644 --- a/content/contracts/v3.x/erc777.mdx +++ b/content/contracts/v3.x/erc777.mdx @@ -1,6 +1,5 @@ --- title: ERC777 -description: ERC777 --- Like [ERC20](erc20.mdx), ERC777 is a standard for [_fungible_ tokens](tokens.mdx#different-kinds-of-tokens), and is focused around allowing more complex interactions when trading tokens. More generally, it brings tokens and Ether closer together by providing the equivalent of a `msg.value` field, but for tokens. diff --git a/content/contracts/v3.x/extending-contracts.mdx b/content/contracts/v3.x/extending-contracts.mdx index 57348335..c9ad5e40 100644 --- a/content/contracts/v3.x/extending-contracts.mdx +++ b/content/contracts/v3.x/extending-contracts.mdx @@ -1,6 +1,5 @@ --- title: Extending Contracts -description: Extending Contracts --- Most of the OpenZeppelin Contracts are expected to be used via [inheritance](https://solidity.readthedocs.io/en/latest/contracts.html#inheritance): you will _inherit_ from them when writing your own contracts. diff --git a/content/contracts/v3.x/gsn-strategies.mdx b/content/contracts/v3.x/gsn-strategies.mdx index fd5424c8..8855a6a1 100644 --- a/content/contracts/v3.x/gsn-strategies.mdx +++ b/content/contracts/v3.x/gsn-strategies.mdx @@ -1,6 +1,5 @@ --- title: GSN Strategies -description: GSN Strategies --- This guide shows you different strategies to accept relayed calls via the Gas Station Network (GSN) using OpenZeppelin Contracts. diff --git a/content/contracts/v3.x/gsn.mdx b/content/contracts/v3.x/gsn.mdx index 15435f06..82466783 100644 --- a/content/contracts/v3.x/gsn.mdx +++ b/content/contracts/v3.x/gsn.mdx @@ -1,6 +1,5 @@ --- title: Writing GSN-capable contracts -description: Writing GSN-capable contracts --- The [Gas Station Network](https://gsn.openzeppelin.com) allows you to build apps where you pay for your users transactions, so they do not need to hold Ether to pay for gas, easing their onboarding process. In this guide, we will learn how to write smart contracts that can receive transactions from the GSN, by using OpenZeppelin Contracts. diff --git a/content/contracts/v3.x/index.mdx b/content/contracts/v3.x/index.mdx index 3907620f..b2bc7b37 100644 --- a/content/contracts/v3.x/index.mdx +++ b/content/contracts/v3.x/index.mdx @@ -1,6 +1,5 @@ --- title: Contracts -description: Contracts --- **A library for secure smart contract development.** Build on a solid foundation of community-vetted code. diff --git a/content/contracts/v3.x/releases-stability.mdx b/content/contracts/v3.x/releases-stability.mdx index 9823afe5..9a6b63b7 100644 --- a/content/contracts/v3.x/releases-stability.mdx +++ b/content/contracts/v3.x/releases-stability.mdx @@ -1,6 +1,5 @@ --- title: New Releases and API Stability -description: New Releases and API Stability --- Developing smart contracts is hard, and a conservative approach towards dependencies is sometimes favored. However, it is also very important to stay on top of new releases: these may include bug fixes, or deprecate old patterns in favor of newer and better practices. diff --git a/content/contracts/v3.x/tokens.mdx b/content/contracts/v3.x/tokens.mdx index 7ad4ecf5..e94c286a 100644 --- a/content/contracts/v3.x/tokens.mdx +++ b/content/contracts/v3.x/tokens.mdx @@ -1,6 +1,5 @@ --- title: Tokens -description: Tokens --- Ah, the "token": blockchain’s most powerful and most misunderstood tool. diff --git a/content/contracts/v3.x/upgradeable.mdx b/content/contracts/v3.x/upgradeable.mdx index cc45f8fd..f94bb9df 100644 --- a/content/contracts/v3.x/upgradeable.mdx +++ b/content/contracts/v3.x/upgradeable.mdx @@ -1,6 +1,5 @@ --- title: Using with Upgrades -description: Using with Upgrades --- If your contract is going to be deployed with upgradeability, such as using the [OpenZeppelin Upgrades Plugins](upgrades-plugins::index.mdx), you will need to use the Upgrade Safe variant of OpenZeppelin Contracts. diff --git a/content/contracts/v3.x/utilities.mdx b/content/contracts/v3.x/utilities.mdx index 23af1081..5854aab6 100644 --- a/content/contracts/v3.x/utilities.mdx +++ b/content/contracts/v3.x/utilities.mdx @@ -1,6 +1,5 @@ --- title: Utilities -description: Utilities --- The OpenZeppelin Contracts provide a ton of useful utilities that you can use in your project. Here are some of the more popular ones. diff --git a/content/contracts/v4.x/access-control.mdx b/content/contracts/v4.x/access-control.mdx index 8600fced..c8ada208 100644 --- a/content/contracts/v4.x/access-control.mdx +++ b/content/contracts/v4.x/access-control.mdx @@ -1,6 +1,5 @@ --- title: Access Control -description: Access Control --- Access control—that is, "who is allowed to do this thing"—is incredibly important in the world of smart contracts. The access control of your contract may govern who can mint tokens, vote on proposals, freeze transfers, and many other things. It is therefore **critical** to understand how you implement it, lest someone else [steals your whole system](https://blog.openzeppelin.com/on-the-parity-wallet-multisig-hack-405a8c12e8f7). diff --git a/content/contracts/v4.x/crosschain.mdx b/content/contracts/v4.x/crosschain.mdx index de1cfa5d..80e9fee4 100644 --- a/content/contracts/v4.x/crosschain.mdx +++ b/content/contracts/v4.x/crosschain.mdx @@ -1,6 +1,5 @@ --- title: Adding cross-chain support to contracts -description: Adding cross-chain support to contracts --- If your contract is targeting to be used in the context of multichain operations, you may need specific tools to identify and process these cross-chain operations. diff --git a/content/contracts/v4.x/crowdsales.mdx b/content/contracts/v4.x/crowdsales.mdx index ac803af9..9240a0b2 100644 --- a/content/contracts/v4.x/crowdsales.mdx +++ b/content/contracts/v4.x/crowdsales.mdx @@ -1,6 +1,5 @@ --- title: Crowdsales -description: Crowdsales --- All crowdsale-related contracts were removed from the OpenZeppelin Contracts library on the [v3.0.0 release](https://forum.openzeppelin.com/t/openzeppelin-contracts-v3-0-beta-release/2256) due to both a decline in their usage and the complexity associated with migrating them to Solidity v0.6. diff --git a/content/contracts/v4.x/drafts.mdx b/content/contracts/v4.x/drafts.mdx index 34a8547b..727211cc 100644 --- a/content/contracts/v4.x/drafts.mdx +++ b/content/contracts/v4.x/drafts.mdx @@ -1,6 +1,5 @@ --- title: Drafts -description: Drafts --- All draft contracts were either moved into a different directory or removed from the OpenZeppelin Contracts library on the [v3.0.0 release](https://forum.openzeppelin.com/t/openzeppelin-contracts-v3-0-beta-release/2256). diff --git a/content/contracts/v4.x/erc1155.mdx b/content/contracts/v4.x/erc1155.mdx index 05cc6dde..26d413e5 100644 --- a/content/contracts/v4.x/erc1155.mdx +++ b/content/contracts/v4.x/erc1155.mdx @@ -1,6 +1,5 @@ --- title: ERC1155 -description: ERC1155 --- ERC1155 is a novel token standard that aims to take the best from previous standards to create a [**fungibility-agnostic**](tokens.mdx#different-kinds-of-tokens) and **gas-efficient** [token contract](tokens.mdx#but_first_coffee_a_primer_on_token_contracts). diff --git a/content/contracts/v4.x/erc20-supply.mdx b/content/contracts/v4.x/erc20-supply.mdx index 3710d073..e6811493 100644 --- a/content/contracts/v4.x/erc20-supply.mdx +++ b/content/contracts/v4.x/erc20-supply.mdx @@ -1,6 +1,5 @@ --- title: Creating ERC20 Supply -description: Creating ERC20 Supply --- In this guide, you will learn how to create an ERC20 token with a custom supply mechanism. We will showcase two idiomatic ways to use OpenZeppelin Contracts for this purpose that you will be able to apply to your smart contract development practice. diff --git a/content/contracts/v4.x/erc20.mdx b/content/contracts/v4.x/erc20.mdx index ce217b70..b2ce8329 100644 --- a/content/contracts/v4.x/erc20.mdx +++ b/content/contracts/v4.x/erc20.mdx @@ -1,6 +1,5 @@ --- title: ERC20 -description: ERC20 --- An ERC20 token contract keeps track of [_fungible_ tokens](tokens.mdx#different-kinds-of-tokens): any one token is exactly equal to any other token; no tokens have special rights or behavior associated with them. This makes ERC20 tokens useful for things like a **medium of exchange currency**, **voting rights**, **staking**, and more. diff --git a/content/contracts/v4.x/erc4626.mdx b/content/contracts/v4.x/erc4626.mdx index a2080284..7b2ccacc 100644 --- a/content/contracts/v4.x/erc4626.mdx +++ b/content/contracts/v4.x/erc4626.mdx @@ -1,6 +1,5 @@ --- title: ERC4626 -description: ERC4626 --- [ERC4626](https://eips.ethereum.org/EIPS/eip-4626) is an extension of [ERC20](erc20.mdx) that proposes a standard interface for token vaults. This standard interface can be used by widely different contracts (including lending markets, aggregators, and intrinsically interest bearing tokens), which brings a number of subtleties. Navigating these potential issues is essential to implementing a compliant and composable token vault. diff --git a/content/contracts/v4.x/erc721.mdx b/content/contracts/v4.x/erc721.mdx index 7c8dda9d..8e2da291 100644 --- a/content/contracts/v4.x/erc721.mdx +++ b/content/contracts/v4.x/erc721.mdx @@ -1,6 +1,5 @@ --- title: ERC721 -description: ERC721 --- We’ve discussed how you can make a _fungible_ token using [ERC20](erc20.mdx), but what if not all tokens are alike? This comes up in situations like **real estate**, **voting rights**, or **collectibles**, where some items are valued more than others, due to their usefulness, rarity, etc. ERC721 is a standard for representing ownership of [_non-fungible_ tokens](tokens.mdx#different-kinds-of-tokens), that is, where each token is unique. diff --git a/content/contracts/v4.x/erc777.mdx b/content/contracts/v4.x/erc777.mdx index 056991e5..53ed9626 100644 --- a/content/contracts/v4.x/erc777.mdx +++ b/content/contracts/v4.x/erc777.mdx @@ -1,6 +1,5 @@ --- title: ERC777 -description: ERC777 --- diff --git a/content/contracts/v4.x/extending-contracts.mdx b/content/contracts/v4.x/extending-contracts.mdx index 63685669..03010ec6 100644 --- a/content/contracts/v4.x/extending-contracts.mdx +++ b/content/contracts/v4.x/extending-contracts.mdx @@ -1,6 +1,5 @@ --- title: Extending Contracts -description: Extending Contracts --- Most of the OpenZeppelin Contracts are expected to be used via [inheritance](https://solidity.readthedocs.io/en/latest/contracts.html#inheritance): you will _inherit_ from them when writing your own contracts. diff --git a/content/contracts/v4.x/governance.mdx b/content/contracts/v4.x/governance.mdx index 9a4789ed..7766dc8f 100644 --- a/content/contracts/v4.x/governance.mdx +++ b/content/contracts/v4.x/governance.mdx @@ -1,6 +1,5 @@ --- title: How to set up on-chain governance -description: How to set up on-chain governance --- In this guide we will learn how OpenZeppelin’s Governor contract works, how to set it up, and how to use it to create proposals, vote for them, and execute them, using tools provided by Ethers.js and Tally. diff --git a/content/contracts/v4.x/index.mdx b/content/contracts/v4.x/index.mdx index 5a311a64..b28a9e23 100644 --- a/content/contracts/v4.x/index.mdx +++ b/content/contracts/v4.x/index.mdx @@ -1,6 +1,5 @@ --- title: Contracts -description: Contracts --- **A library for secure smart contract development.** Build on a solid foundation of community-vetted code. diff --git a/content/contracts/v4.x/releases-stability.mdx b/content/contracts/v4.x/releases-stability.mdx index 536c14d8..f4e298d6 100644 --- a/content/contracts/v4.x/releases-stability.mdx +++ b/content/contracts/v4.x/releases-stability.mdx @@ -1,6 +1,5 @@ --- title: New Releases and API Stability -description: New Releases and API Stability --- Developing smart contracts is hard, and a conservative approach towards dependencies is sometimes favored. However, it is also very important to stay on top of new releases: these may include bug fixes, or deprecate old patterns in favor of newer and better practices. diff --git a/content/contracts/v4.x/tokens.mdx b/content/contracts/v4.x/tokens.mdx index d5efda1a..3ad423dd 100644 --- a/content/contracts/v4.x/tokens.mdx +++ b/content/contracts/v4.x/tokens.mdx @@ -1,6 +1,5 @@ --- title: Tokens -description: Tokens --- Ah, the "token": blockchain’s most powerful and most misunderstood tool. diff --git a/content/contracts/v4.x/upgradeable.mdx b/content/contracts/v4.x/upgradeable.mdx index 6429ec39..135a7408 100644 --- a/content/contracts/v4.x/upgradeable.mdx +++ b/content/contracts/v4.x/upgradeable.mdx @@ -1,6 +1,5 @@ --- title: Using with Upgrades -description: Using with Upgrades --- If your contract is going to be deployed with upgradeability, such as using the [OpenZeppelin Upgrades Plugins](upgrades-plugins::index.mdx), you will need to use the Upgradeable variant of OpenZeppelin Contracts. diff --git a/content/contracts/v4.x/utilities.mdx b/content/contracts/v4.x/utilities.mdx index 441ea7c4..155b1f9c 100644 --- a/content/contracts/v4.x/utilities.mdx +++ b/content/contracts/v4.x/utilities.mdx @@ -1,6 +1,5 @@ --- title: Utilities -description: Utilities --- The OpenZeppelin Contracts provide a ton of useful utilities that you can use in your project. Here are some of the more popular ones. diff --git a/content/contracts/v5.x/access-control.mdx b/content/contracts/v5.x/access-control.mdx index 8dacf7d5..6fbb6435 100644 --- a/content/contracts/v5.x/access-control.mdx +++ b/content/contracts/v5.x/access-control.mdx @@ -1,6 +1,5 @@ --- title: Access Control -description: Access Control --- Access control—that is, "who is allowed to do this thing"—is incredibly important in the world of smart contracts. The access control of your contract may govern who can mint tokens, vote on proposals, freeze transfers, and many other things. It is therefore **critical** to understand how you implement it, lest someone else [steals your whole system](https://blog.openzeppelin.com/on-the-parity-wallet-multisig-hack-405a8c12e8f7). diff --git a/content/contracts/v5.x/account-abstraction.mdx b/content/contracts/v5.x/account-abstraction.mdx index 38ba4b7d..9362e044 100644 --- a/content/contracts/v5.x/account-abstraction.mdx +++ b/content/contracts/v5.x/account-abstraction.mdx @@ -1,6 +1,5 @@ --- title: Account Abstraction -description: Account Abstraction --- Unlike Externally Owned Accounts (EOAs), smart contracts may contain arbitrary verification logic based on authentication mechanisms different to Ethereum’s native [ECDSA](contracts/v5.x/api/utils.mdx#ECDSA) and have execution advantages such as batching or gas sponsorship. To leverage these properties of smart contracts, the community has widely adopted [ERC-4337](https://eips.ethereum.org/EIPS/eip-4337), a standard to process user operations through an alternative mempool. diff --git a/content/contracts/v5.x/accounts.mdx b/content/contracts/v5.x/accounts.mdx index a083fce0..2354fb1b 100644 --- a/content/contracts/v5.x/accounts.mdx +++ b/content/contracts/v5.x/accounts.mdx @@ -1,6 +1,5 @@ --- title: Smart Accounts -description: Smart Accounts --- OpenZeppelin provides a simple [`Account`](contracts/v5.x/api/account.mdx#Account) implementation including only the basic logic to handle user operations in compliance with ERC-4337. Developers who want to build their own account can leverage it to bootstrap custom implementations. diff --git a/content/contracts/v5.x/backwards-compatibility.mdx b/content/contracts/v5.x/backwards-compatibility.mdx index 77aafdec..7535ff1d 100644 --- a/content/contracts/v5.x/backwards-compatibility.mdx +++ b/content/contracts/v5.x/backwards-compatibility.mdx @@ -1,6 +1,5 @@ --- title: Backwards Compatibility -description: Backwards Compatibility --- OpenZeppelin Contracts uses semantic versioning to communicate backwards compatibility of its API and storage layout. Patch and minor updates will generally be backwards compatible, with rare exceptions as detailed below. Major updates should be assumed incompatible with previous releases. On this page, we provide details about these guarantees. diff --git a/content/contracts/v5.x/eoa-delegation.mdx b/content/contracts/v5.x/eoa-delegation.mdx index 0c7b669e..3c5a366e 100644 --- a/content/contracts/v5.x/eoa-delegation.mdx +++ b/content/contracts/v5.x/eoa-delegation.mdx @@ -1,6 +1,5 @@ --- title: EOA Delegation -description: EOA Delegation --- [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702) introduces a new transaction type (`0x4`) that grants [Externally Owned Accounts (EOAs)](https://ethereum.org/en/developers/docs/accounts/) the ability to delegate execution to an smart contract. This is particularly useful to enable traditional EVM accounts to: diff --git a/content/contracts/v5.x/erc1155.mdx b/content/contracts/v5.x/erc1155.mdx index eb9103fb..d77f1939 100644 --- a/content/contracts/v5.x/erc1155.mdx +++ b/content/contracts/v5.x/erc1155.mdx @@ -1,6 +1,5 @@ --- title: ERC-1155 -description: ERC-1155 --- ERC-1155 is a novel token standard that aims to take the best from previous standards to create a [**fungibility-agnostic**](tokens.adoc#different-kinds-of-tokens) and **gas-efficient** [token contract](tokens.adoc#but_first_coffee_a_primer_on_token_contracts). diff --git a/content/contracts/v5.x/erc20-supply.mdx b/content/contracts/v5.x/erc20-supply.mdx index b920c0cd..443e4ab0 100644 --- a/content/contracts/v5.x/erc20-supply.mdx +++ b/content/contracts/v5.x/erc20-supply.mdx @@ -1,6 +1,5 @@ --- title: Creating ERC-20 Supply -description: Creating ERC-20 Supply --- In this guide, you will learn how to create an ERC-20 token with a custom supply mechanism. We will showcase two idiomatic ways to use OpenZeppelin Contracts for this purpose that you will be able to apply to your smart contract development practice. diff --git a/content/contracts/v5.x/erc20.mdx b/content/contracts/v5.x/erc20.mdx index 364366bb..1e4e0a73 100644 --- a/content/contracts/v5.x/erc20.mdx +++ b/content/contracts/v5.x/erc20.mdx @@ -1,6 +1,5 @@ --- title: ERC-20 -description: ERC-20 --- An ERC-20 token contract keeps track of [_fungible_ tokens](tokens.adoc#different-kinds-of-tokens): any one token is exactly equal to any other token; no tokens have special rights or behavior associated with them. This makes ERC-20 tokens useful for things like a **medium of exchange currency**, **voting rights**, **staking**, and more. diff --git a/content/contracts/v5.x/erc4626.mdx b/content/contracts/v5.x/erc4626.mdx index 569f59e2..262fbcfe 100644 --- a/content/contracts/v5.x/erc4626.mdx +++ b/content/contracts/v5.x/erc4626.mdx @@ -1,6 +1,5 @@ --- title: ERC-4626 -description: ERC-4626 --- [ERC-4626](https://eips.ethereum.org/EIPS/eip-4626) is an extension of [ERC-20](erc20.adoc) that proposes a standard interface for token vaults. This standard interface can be used by widely different contracts (including lending markets, aggregators, and intrinsically interest bearing tokens), which brings a number of subtleties. Navigating these potential issues is essential to implementing a compliant and composable token vault. diff --git a/content/contracts/v5.x/erc6909.mdx b/content/contracts/v5.x/erc6909.mdx index 75922041..8b4f4c1f 100644 --- a/content/contracts/v5.x/erc6909.mdx +++ b/content/contracts/v5.x/erc6909.mdx @@ -1,6 +1,5 @@ --- title: ERC-6909 -description: ERC-6909 --- ERC-6909 is a draft EIP that draws on ERC-1155 learnings since it was published in 2018. The main goals of ERC-6909 is to decrease gas costs and complexity--this is mainly accomplished by removing batching and callbacks. diff --git a/content/contracts/v5.x/erc721.mdx b/content/contracts/v5.x/erc721.mdx index 22bf5160..8340c0e0 100644 --- a/content/contracts/v5.x/erc721.mdx +++ b/content/contracts/v5.x/erc721.mdx @@ -1,6 +1,5 @@ --- title: ERC-721 -description: ERC-721 --- We’ve discussed how you can make a _fungible_ token using [ERC-20](erc20.adoc), but what if not all tokens are alike? This comes up in situations like **real estate**, **voting rights**, or **collectibles**, where some items are valued more than others, due to their usefulness, rarity, etc. ERC-721 is a standard for representing ownership of [_non-fungible_ tokens](tokens.adoc#different-kinds-of-tokens), that is, where each token is unique. diff --git a/content/contracts/v5.x/extending-contracts.mdx b/content/contracts/v5.x/extending-contracts.mdx index f70c7df2..3f90f012 100644 --- a/content/contracts/v5.x/extending-contracts.mdx +++ b/content/contracts/v5.x/extending-contracts.mdx @@ -1,6 +1,5 @@ --- title: Extending Contracts -description: Extending Contracts --- Most of the OpenZeppelin Contracts are expected to be used via [inheritance](https://solidity.readthedocs.io/en/latest/contracts.html#inheritance): you will _inherit_ from them when writing your own contracts. diff --git a/content/contracts/v5.x/faq.mdx b/content/contracts/v5.x/faq.mdx index 79ecb09f..7858ccd0 100644 --- a/content/contracts/v5.x/faq.mdx +++ b/content/contracts/v5.x/faq.mdx @@ -1,6 +1,5 @@ --- title: Frequently Asked Questions -description: Frequently Asked Questions --- ## Can I restrict a function to EOAs only? diff --git a/content/contracts/v5.x/governance.mdx b/content/contracts/v5.x/governance.mdx index f07668cb..fde891a3 100644 --- a/content/contracts/v5.x/governance.mdx +++ b/content/contracts/v5.x/governance.mdx @@ -1,6 +1,5 @@ --- title: How to set up on-chain governance -description: How to set up on-chain governance --- In this guide we will learn how OpenZeppelin’s Governor contract works, how to set it up, and how to use it to create proposals, vote for them, and execute them, using tools provided by Ethers.js and Tally. diff --git a/content/contracts/v5.x/learn/building-a-dapp.mdx b/content/contracts/v5.x/learn/building-a-dapp.mdx index 05639316..dcd835fb 100644 --- a/content/contracts/v5.x/learn/building-a-dapp.mdx +++ b/content/contracts/v5.x/learn/building-a-dapp.mdx @@ -1,6 +1,5 @@ --- title: Building a dapp -description: Building a dapp --- diff --git a/content/contracts/v5.x/learn/connecting-to-public-test-networks.mdx b/content/contracts/v5.x/learn/connecting-to-public-test-networks.mdx index 21aeab80..8f5bcd0a 100644 --- a/content/contracts/v5.x/learn/connecting-to-public-test-networks.mdx +++ b/content/contracts/v5.x/learn/connecting-to-public-test-networks.mdx @@ -1,6 +1,5 @@ --- title: Connecting to public test networks -description: Connecting to public test networks --- After you have [written your contracts](developing-smart-contracts.mdx), and [tried them out locally](deploying-and-interacting.mdx) and [tested them thoroughly](writing-automated-tests.mdx), it’s time to move to a persistent public testing environment, where you and your beta users can start interacting with your application. diff --git a/content/contracts/v5.x/learn/deploying-and-interacting.mdx b/content/contracts/v5.x/learn/deploying-and-interacting.mdx index 2cc38a82..304ae7bd 100644 --- a/content/contracts/v5.x/learn/deploying-and-interacting.mdx +++ b/content/contracts/v5.x/learn/deploying-and-interacting.mdx @@ -1,6 +1,5 @@ --- title: Deploying and interacting with smart contracts -description: Deploying and interacting with smart contracts --- Unlike most software, smart contracts don’t run on your computer or somebody’s server: they live on the Ethereum network itself. This means that interacting with them is a bit different from more traditional applications. diff --git a/content/contracts/v5.x/learn/developing-smart-contracts.mdx b/content/contracts/v5.x/learn/developing-smart-contracts.mdx index cbedc411..1d4d0b2d 100644 --- a/content/contracts/v5.x/learn/developing-smart-contracts.mdx +++ b/content/contracts/v5.x/learn/developing-smart-contracts.mdx @@ -1,6 +1,5 @@ --- title: Developing smart contracts -description: Developing smart contracts --- Welcome to the exciting world of smart contract development! This guide will let you get started writing Solidity contracts by going over the following: diff --git a/content/contracts/v5.x/learn/index.mdx b/content/contracts/v5.x/learn/index.mdx index 9d090a43..170d4af4 100644 --- a/content/contracts/v5.x/learn/index.mdx +++ b/content/contracts/v5.x/learn/index.mdx @@ -1,6 +1,5 @@ --- title: Learn -description: Learn --- Comprehensive guides for every step of your development journey. diff --git a/content/contracts/v5.x/learn/preparing-for-mainnet.mdx b/content/contracts/v5.x/learn/preparing-for-mainnet.mdx index 3c9f050a..2d4ee3df 100644 --- a/content/contracts/v5.x/learn/preparing-for-mainnet.mdx +++ b/content/contracts/v5.x/learn/preparing-for-mainnet.mdx @@ -1,6 +1,5 @@ --- title: Preparing for mainnet -description: Preparing for mainnet --- After [running your project on a testnet](connecting-to-public-test-networks.mdx) for some time without issues, you will want to deploy it to the main Ethereum network (aka _mainnet_). However, the planning for going to mainnet should begin much earlier than your planned release date. diff --git a/content/contracts/v5.x/learn/sending-gasless-transactions.mdx b/content/contracts/v5.x/learn/sending-gasless-transactions.mdx index 5e3b79b8..0206cc14 100644 --- a/content/contracts/v5.x/learn/sending-gasless-transactions.mdx +++ b/content/contracts/v5.x/learn/sending-gasless-transactions.mdx @@ -1,6 +1,5 @@ --- title: Sending gasless transactions -description: Sending gasless transactions --- diff --git a/content/contracts/v5.x/learn/setting-up-a-node-project.mdx b/content/contracts/v5.x/learn/setting-up-a-node-project.mdx index ad54f796..5d5d1b0a 100644 --- a/content/contracts/v5.x/learn/setting-up-a-node-project.mdx +++ b/content/contracts/v5.x/learn/setting-up-a-node-project.mdx @@ -1,6 +1,5 @@ --- title: Setting up a Node project -description: Setting up a Node project --- New software industries often start out with every project sharing the same technology stack. The Ethereum ecosystem is no exception, and the language of choice is [JavaScript](https://en.wikipedia.org/wiki/JavaScript). Many Ethereum libraries, including OpenZeppelin software, are written in JavaScript or one of its variants. diff --git a/content/contracts/v5.x/learn/upgrading-smart-contracts.mdx b/content/contracts/v5.x/learn/upgrading-smart-contracts.mdx index 5624df98..8a74df10 100644 --- a/content/contracts/v5.x/learn/upgrading-smart-contracts.mdx +++ b/content/contracts/v5.x/learn/upgrading-smart-contracts.mdx @@ -1,6 +1,5 @@ --- title: Upgrading smart contracts -description: Upgrading smart contracts --- Smart contracts deployed using [OpenZeppelin Upgrades Plugins](upgrades-plugins::index.mdx) can be ***upgraded*** to modify their code, while preserving their address, state, and balance. This allows you to iteratively add new features to your project, or fix any bugs you may find [in production](preparing-for-mainnet.mdx). diff --git a/content/contracts/v5.x/learn/writing-automated-tests.mdx b/content/contracts/v5.x/learn/writing-automated-tests.mdx index eb31bf6b..11accff1 100644 --- a/content/contracts/v5.x/learn/writing-automated-tests.mdx +++ b/content/contracts/v5.x/learn/writing-automated-tests.mdx @@ -1,6 +1,5 @@ --- title: Writing automated smart contract tests -description: Writing automated smart contract tests --- In a blockchain environment, a single mistake could cost you all of your funds - or even worse, your users' funds! This guide will help you develop robust applications by writing automated tests that verify your application behaves exactly as you intended. diff --git a/content/contracts/v5.x/multisig.mdx b/content/contracts/v5.x/multisig.mdx index d4e5d4e0..3c1f4b5b 100644 --- a/content/contracts/v5.x/multisig.mdx +++ b/content/contracts/v5.x/multisig.mdx @@ -1,6 +1,5 @@ --- title: Multisig Account -description: Multisig Account --- A multi-signature (multisig) account is a smart account that requires multiple authorized signers to approve operations before execution. Unlike traditional accounts controlled by a single private key, multisigs distribute control among multiple parties, eliminating single points of failure. For example, a 2-of-3 multisig requires signatures from at least 2 out of 3 possible signers. diff --git a/content/contracts/v5.x/tokens.mdx b/content/contracts/v5.x/tokens.mdx index 909b5f5c..08de7767 100644 --- a/content/contracts/v5.x/tokens.mdx +++ b/content/contracts/v5.x/tokens.mdx @@ -1,6 +1,5 @@ --- title: Tokens -description: Tokens --- Ah, the "token": blockchain’s most powerful and most misunderstood tool. diff --git a/content/contracts/v5.x/upgradeable.mdx b/content/contracts/v5.x/upgradeable.mdx index cf9ff122..2c7ad167 100644 --- a/content/contracts/v5.x/upgradeable.mdx +++ b/content/contracts/v5.x/upgradeable.mdx @@ -1,6 +1,5 @@ --- title: Using with Upgrades -description: Using with Upgrades --- If your contract is going to be deployed with upgradeability, such as using the [OpenZeppelin Upgrades Plugins](upgrades-plugins::index.adoc), you will need to use the Upgradeable variant of OpenZeppelin Contracts. diff --git a/content/contracts/v5.x/wizard.mdx b/content/contracts/v5.x/wizard.mdx index 8d7ae261..8b1db9be 100644 --- a/content/contracts/v5.x/wizard.mdx +++ b/content/contracts/v5.x/wizard.mdx @@ -1,6 +1,5 @@ --- title: Contracts Wizard -description: Contracts Wizard --- Not sure where to start? Use the interactive generator below to bootstrap your diff --git a/content/defender/dac.mdx b/content/defender/dac.mdx index ad84779d..3f0ce126 100644 --- a/content/defender/dac.mdx +++ b/content/defender/dac.mdx @@ -1,6 +1,5 @@ --- title: Defender as Code Plugin -description: Defender as Code Plugin --- Defender as Code (DaC) is a Serverless Framework plugin for automated resource management and configuration as code. diff --git a/content/defender/faq.mdx b/content/defender/faq.mdx index a824cd39..81c759d7 100644 --- a/content/defender/faq.mdx +++ b/content/defender/faq.mdx @@ -1,6 +1,5 @@ --- title: Frequently Asked Questions (FAQ) -description: Frequently Asked Questions (FAQ) --- OpenZeppelin Defender is the evolution of Defender, with an improved user experience, a cleaner interface, and new features that offer a more cohesive experience across the DevSecOps lifecycle. diff --git a/content/defender/guide/factory-monitor.mdx b/content/defender/guide/factory-monitor.mdx index 8300de4c..fe7478e0 100644 --- a/content/defender/guide/factory-monitor.mdx +++ b/content/defender/guide/factory-monitor.mdx @@ -1,6 +1,5 @@ --- title: Automatic monitoring for factory clones -description: Automatic monitoring for factory clones --- The factory-clone pattern can be advantageous for minimizing gas costs. However, since each clone gets deployed to a new address, it may be a challenge to efficiently track and monitor each of these contracts. diff --git a/content/defender/guide/fireblock-defender-integration.mdx b/content/defender/guide/fireblock-defender-integration.mdx index c0bdf08f..194d0897 100644 --- a/content/defender/guide/fireblock-defender-integration.mdx +++ b/content/defender/guide/fireblock-defender-integration.mdx @@ -1,6 +1,5 @@ --- title: Fireblocks integration within Defender -description: Fireblocks integration within Defender --- You can directly submit transactions to Fireblocks from Defender. Fireblocks is a robust asset management solution that utilizes multi-party computation to secure all treasury operations, ensuring enhanced security and efficiency. diff --git a/content/defender/guide/forked-network.mdx b/content/defender/guide/forked-network.mdx index 766b2ddb..7de350ef 100644 --- a/content/defender/guide/forked-network.mdx +++ b/content/defender/guide/forked-network.mdx @@ -1,6 +1,5 @@ --- title: Deploy a smart contract on a forked network -description: Deploy a smart contract on a forked network --- Defender empowers you to harness your customized network forks for deploying and testing smart contracts, along with associated configurations of, for example, actions, monitors, and workflows. This guide will lead you through the steps of deploying a smart contract on a forked network and interacting with it. diff --git a/content/defender/guide/meta-tx.mdx b/content/defender/guide/meta-tx.mdx index 4dfbe5ca..1b22cfa3 100644 --- a/content/defender/guide/meta-tx.mdx +++ b/content/defender/guide/meta-tx.mdx @@ -1,6 +1,5 @@ --- title: Relaying gasless meta-transactions with a web app -description: Relaying gasless meta-transactions with a web app --- Gasless meta-transactions offer users a more seamless experience on the blockchain, potentially eliminating the need to spend money on gas fees for every interaction. This method allows users to sign a transaction for free and have it securely executed by a third party, with that party paying the gas to complete the transaction. diff --git a/content/defender/guide/private-network.mdx b/content/defender/guide/private-network.mdx index 5d872ef0..3ec16d53 100644 --- a/content/defender/guide/private-network.mdx +++ b/content/defender/guide/private-network.mdx @@ -1,6 +1,5 @@ --- title: Adding a complete Private Network -description: Adding a complete Private Network --- Private Networks allow you to customize your account by adding compatible mainnets and testnets. You can then use them as any other supported network to deploy, monitor, and manage smart contracts on those networks. This guide will lead you through the steps of adding a Private Network with a subgraph and Safe contracts. diff --git a/content/defender/guide/timelock-roles.mdx b/content/defender/guide/timelock-roles.mdx index 9cbaa6a1..29eb20f9 100644 --- a/content/defender/guide/timelock-roles.mdx +++ b/content/defender/guide/timelock-roles.mdx @@ -1,6 +1,5 @@ --- title: How to manage roles of a TimelockController -description: How to manage roles of a TimelockController --- Defender allows you to oversee and command contract permissions of any smart contract that uses [AccessControl from OpenZeppelin Contracts, window=_blank](https://docs.openzeppelin.com/contracts/api/access#AccessControl). This guide will lead you through the steps of importing a TimelockController contract, creating a proposal, and managing its roles. A `TimelockController` is a smart contract that enforces a delay between when an operation is queued and when it can be executed. This mechanism is commonly used in decentralized governance to increase security and provide transparency, allowing stakeholders to observe and react to changes before they are executed. TimelockController uses the following `AccessControl`` setup: diff --git a/content/defender/guide/upgrade-actions-dependencies.mdx b/content/defender/guide/upgrade-actions-dependencies.mdx index ae243482..7cf65060 100644 --- a/content/defender/guide/upgrade-actions-dependencies.mdx +++ b/content/defender/guide/upgrade-actions-dependencies.mdx @@ -1,6 +1,5 @@ --- title: Upgrading Actions Dependencies -description: Upgrading Actions Dependencies --- Actions must be kept updated with the latest Node.js runtime and dependencies versions to ensure they run in an up-to-date and secure environment. Occasionally, Node.js and dependencies versions get deprecated on Defender, which means that the Actions running on those Node.js versions (and related dependencies) must be upgraded to the latest ones to ensure they continue to function as expected. diff --git a/content/defender/guide/usage-notification.mdx b/content/defender/guide/usage-notification.mdx index 53ad3cb2..5cd473aa 100644 --- a/content/defender/guide/usage-notification.mdx +++ b/content/defender/guide/usage-notification.mdx @@ -1,6 +1,5 @@ --- title: Manage custom and system usage notifications -description: Manage custom and system usage notifications --- Defender enables you to receive notifications when your usage exceeds a certain threshold. This guide will walk you through the steps of setting up custom usage notifications and managing system usage notifications. Defender currently tracks usage metrics such as: diff --git a/content/defender/index.mdx b/content/defender/index.mdx index f8c4a29e..96d2367a 100644 --- a/content/defender/index.mdx +++ b/content/defender/index.mdx @@ -1,6 +1,5 @@ --- title: Defender -description: Defender --- diff --git a/content/defender/integrations.mdx b/content/defender/integrations.mdx index 5de16f1d..a142ef70 100644 --- a/content/defender/integrations.mdx +++ b/content/defender/integrations.mdx @@ -1,6 +1,5 @@ --- title: Integrations -description: Integrations --- Defender seamlessly integrates with your existing tools and workflows, so you can easily secure your project throughout the secure development lifecycle. diff --git a/content/defender/logs.mdx b/content/defender/logs.mdx index 65347826..0943bea4 100644 --- a/content/defender/logs.mdx +++ b/content/defender/logs.mdx @@ -1,6 +1,5 @@ --- title: Logs -description: Logs --- Defender generates log trails of every potentially relevant event in the system. This includes manual actions, such as modifying an Action or Monitor, as well as automated activity, such as sending a transaction or firing a notification. Logs can be optionally forwarded to Datadog and Splunk for aggregation or exported. diff --git a/content/defender/module/access-control.mdx b/content/defender/module/access-control.mdx index 5b8ce49a..8c339442 100644 --- a/content/defender/module/access-control.mdx +++ b/content/defender/module/access-control.mdx @@ -1,6 +1,5 @@ --- title: Access Control -description: Access Control --- Access control allows you to seamlessly oversee and command contract permissions on a grand scale, with the power to view and control access at a granular level. Currently supports [ownable, window=_blank](https://docs.openzeppelin.com/contracts/4.x/access-control#ownership-and-ownable) and [role-based, window=_blank](https://docs.openzeppelin.com/contracts/4.x/access-control#role-based-access-control) access control. diff --git a/content/defender/module/actions.mdx b/content/defender/module/actions.mdx index 5b6ab6a7..3aaa35fe 100644 --- a/content/defender/module/actions.mdx +++ b/content/defender/module/actions.mdx @@ -1,6 +1,5 @@ --- title: Actions -description: Actions --- Actions allow you to implement custom app logic for on-chain and off-chain operations. You can enable automated responses to threats detected by the [Monitor](module/monitor.mdx) and [Workflows](module/actions.mdx#workflows) modules. diff --git a/content/defender/module/address-book.mdx b/content/defender/module/address-book.mdx index be1d1bc0..6ecdc667 100644 --- a/content/defender/module/address-book.mdx +++ b/content/defender/module/address-book.mdx @@ -1,6 +1,5 @@ --- title: Address Book -description: Address Book --- The Address Book allows you to create a shared repository of user-friendly names for your accounts or contracts. You can set up these names anywhere you see an address in Defender just by clicking on it, or you can manage your entire Address Book in the dedicated section. Defender automatically creates Address Book entries for you when you import accounts and contracts in other modules. diff --git a/content/defender/module/audit.mdx b/content/defender/module/audit.mdx index 7293645f..cbab1d31 100644 --- a/content/defender/module/audit.mdx +++ b/content/defender/module/audit.mdx @@ -1,6 +1,5 @@ --- title: Audit -description: Audit --- Audit allows you to summon our team of security experts to verify your system works as intended with the highest level of scrutiny. You can track issues and resolutions, and interact directly with auditors for faster and more efficient communication. diff --git a/content/defender/module/code.mdx b/content/defender/module/code.mdx index e095ce36..bdebf9e7 100644 --- a/content/defender/module/code.mdx +++ b/content/defender/module/code.mdx @@ -1,6 +1,5 @@ --- title: Code Inspector -description: Code Inspector --- Code Inspector seamlessly integrates with Github to maximize security with every step of your development process via automatic code analysis powered by machine learning intelligence and state-of-the-art tools developed by our security experts. diff --git a/content/defender/module/deploy.mdx b/content/defender/module/deploy.mdx index 3b50062c..de29d3e9 100644 --- a/content/defender/module/deploy.mdx +++ b/content/defender/module/deploy.mdx @@ -1,6 +1,5 @@ --- title: Deploy -description: Deploy --- Deploy allows you to deploy and upgrade smart contracts across chains securely. You can prove that the code running on-chain matches the audited implementation and minimize crucial mistakes that can lead to losses or issues. diff --git a/content/defender/module/monitor.mdx b/content/defender/module/monitor.mdx index 214aa585..16c45bc7 100644 --- a/content/defender/module/monitor.mdx +++ b/content/defender/module/monitor.mdx @@ -1,6 +1,5 @@ --- title: Monitor -description: Monitor --- Monitors allow you to gain full visibility into your smart contracts' risks and behaviors. You can detect threats, get alerts on threats and anomalies, and automatically respond and resolve issues. diff --git a/content/defender/module/relayers.mdx b/content/defender/module/relayers.mdx index d5b149f4..90a6152c 100644 --- a/content/defender/module/relayers.mdx +++ b/content/defender/module/relayers.mdx @@ -1,6 +1,5 @@ --- title: Relayers -description: Relayers --- Relayers allow you to send on-chain transactions via regular API requests or through other Defender modules, like Actions, Workflows, and Deploy. Relayers also automate the payment of gas fees and take care of private key secure storage, transaction signing, nonce management, gas pricing estimation, and resubmissions. With Relayers, you don’t have to worry about storing private keys on your back-end servers or monitoring gas prices and transactions to ensure they get confirmed. diff --git a/content/defender/module/transaction-proposals.mdx b/content/defender/module/transaction-proposals.mdx index e1d6d857..8435ce77 100644 --- a/content/defender/module/transaction-proposals.mdx +++ b/content/defender/module/transaction-proposals.mdx @@ -1,6 +1,5 @@ --- title: Transaction Proposals -description: Transaction Proposals --- Transaction Proposals are very similar to actions, but instead of having to write the javascript code, you can use a form-based editor to define the transaction parameters.\ diff --git a/content/defender/remix-plugin.mdx b/content/defender/remix-plugin.mdx index 42344953..72f28a40 100644 --- a/content/defender/remix-plugin.mdx +++ b/content/defender/remix-plugin.mdx @@ -1,6 +1,5 @@ --- title: Remix Plugin -description: Remix Plugin --- When coding and compiling contracts from [Remix IDE, window=_blank](https://remix.ethereum.org/), you can use Defender Plugin to deploy your contracts by configuring a [Deployment Environment](module/deploy.mdx) and using an Approval Process as deployer. diff --git a/content/defender/sdk.mdx b/content/defender/sdk.mdx index 7ab9cda1..c3932aa3 100644 --- a/content/defender/sdk.mdx +++ b/content/defender/sdk.mdx @@ -1,6 +1,5 @@ --- title: Defender SDK and API -description: Defender SDK and API --- [Defender SDK, window=_blank](https://www.npmjs.com/package/@openzeppelin/defender-sdk) (Formerly defender-client packages) is a node package that allows developers to interact with Defender programatically using Javascript/Typescript. diff --git a/content/defender/settings.mdx b/content/defender/settings.mdx index 8f4fdc57..33afb241 100644 --- a/content/defender/settings.mdx +++ b/content/defender/settings.mdx @@ -1,6 +1,5 @@ --- title: Settings -description: Settings --- Manage everything related to the configuration of Defender, including notifications, addresses, team members, API keys, and more. diff --git a/content/defender/settings/notifications.mdx b/content/defender/settings/notifications.mdx index e489f4d8..ae547626 100644 --- a/content/defender/settings/notifications.mdx +++ b/content/defender/settings/notifications.mdx @@ -1,6 +1,5 @@ --- title: Notification Channels -description: Notification Channels --- Use Notification Channels to get notified about events across different Defender Modules, like Monitor Triggers, Workflows or Relayer Transactions lifecycle events. diff --git a/content/defender/tutorial/access-control.mdx b/content/defender/tutorial/access-control.mdx index 2bcf8e4f..a113feef 100644 --- a/content/defender/tutorial/access-control.mdx +++ b/content/defender/tutorial/access-control.mdx @@ -1,6 +1,5 @@ --- title: Modify and assign roles in a role-based access control smart contract -description: Modify and assign roles in a role-based access control smart contract --- Defender allows you to seamlessly oversee and command contract permissions on a grand scale, with the power to view and control access at a granular level. This tutorial shows how to add a smart contract to see and manage its roles, including assigning and removing roles. diff --git a/content/defender/tutorial/actions.mdx b/content/defender/tutorial/actions.mdx index 2853afa0..0db76605 100644 --- a/content/defender/tutorial/actions.mdx +++ b/content/defender/tutorial/actions.mdx @@ -1,6 +1,5 @@ --- title: Automate smart contract transaction for hourly activity -description: Automate smart contract transaction for hourly activity --- Defender allows you to automate smart contract operational tasks with easy integration with the rest of Defender. This tutorial shows how to build an action that sends an on-chain transaction every hour that adds an object to a box and increases the number of objects inside. diff --git a/content/defender/tutorial/deploy.mdx b/content/defender/tutorial/deploy.mdx index 0e33f4b7..25494ca0 100644 --- a/content/defender/tutorial/deploy.mdx +++ b/content/defender/tutorial/deploy.mdx @@ -1,6 +1,5 @@ --- title: Securely deploy and upgrade a smart contract -description: Securely deploy and upgrade a smart contract --- Defender allows you to easily deploy and upgrade smart contracts across chains while maintaining the best security practices. This tutorial shows how to use a [Relayer](:settings.adoc#relayers) to deploy a contract called Box and upgrade it with the UUPS proxy pattern via a [Safe wallet, window=_blank](https://safe.global/) (multisig). diff --git a/content/defender/tutorial/monitor.mdx b/content/defender/tutorial/monitor.mdx index 60887d4a..a45cca7e 100644 --- a/content/defender/tutorial/monitor.mdx +++ b/content/defender/tutorial/monitor.mdx @@ -1,6 +1,5 @@ --- title: Monitor a smart contract for on-chain activity -description: Monitor a smart contract for on-chain activity --- Defender allows you to monitor smart contract transactions and events across chains. This tutorial shows how to build a customized Monitor template and use it in a real-world context to monitor a [Uniswap V2, window=_blank](https://uniswap.org/) pool. diff --git a/content/defender/tutorial/relayer.mdx b/content/defender/tutorial/relayer.mdx index 792f93eb..0c9c3ec0 100644 --- a/content/defender/tutorial/relayer.mdx +++ b/content/defender/tutorial/relayer.mdx @@ -1,6 +1,5 @@ --- title: Using Relayer for Sending Transactions -description: Using Relayer for Sending Transactions --- In this tutorial, we will explore how to use the [Relayers](module/relayers.mdx) to send transactions. We will cover: diff --git a/content/defender/tutorial/workflows.mdx b/content/defender/tutorial/workflows.mdx index 77da489a..e13b4689 100644 --- a/content/defender/tutorial/workflows.mdx +++ b/content/defender/tutorial/workflows.mdx @@ -1,6 +1,5 @@ --- title: Create an Action Workflow to decrease the number of objects in a Box contract -description: Create an Action Workflow to decrease the number of objects in a Box contract --- Defender allows you to target and activate on-chain activity using Action Workflow quickly. This tutorial shows how to create a workflow that monitors the number of objects in a Box contract and executes an action when an object is added to it. diff --git a/content/defender/wizard-plugin.mdx b/content/defender/wizard-plugin.mdx index b9264021..daf41b82 100644 --- a/content/defender/wizard-plugin.mdx +++ b/content/defender/wizard-plugin.mdx @@ -1,6 +1,5 @@ --- title: Contracts Wizard Deploy Plugin -description: Contracts Wizard Deploy Plugin --- When configuring contracts from [Contracts Wizard, window=_blank](https://wizard.openzeppelin.com/), you can directly deploy the configured Smart Contract using your Defender account. diff --git a/content/openzeppelin-monitor/architecture.mdx b/content/openzeppelin-monitor/architecture.mdx index 7dbc53ca..8abd445e 100644 --- a/content/openzeppelin-monitor/architecture.mdx +++ b/content/openzeppelin-monitor/architecture.mdx @@ -1,6 +1,5 @@ --- title: Architecture Guide -description: Architecture Guide --- This document describes the high-level architecture of OpenZeppelin Monitor, including the core components, their interactions, and the overall system design. It provides a technical overview of how the service processes blockchain data and triggers notifications based on configurable conditions. diff --git a/content/openzeppelin-monitor/contribution.mdx b/content/openzeppelin-monitor/contribution.mdx index b65ce9f3..1b51b4d3 100644 --- a/content/openzeppelin-monitor/contribution.mdx +++ b/content/openzeppelin-monitor/contribution.mdx @@ -1,6 +1,5 @@ --- title: Contribution Guidelines -description: Contribution Guidelines --- Welcome to the OpenZeppelin Monitor project! We appreciate your interest in contributing. This guide outlines the requirements and processes for contributing to the project. diff --git a/content/openzeppelin-monitor/error.mdx b/content/openzeppelin-monitor/error.mdx index 1a01489e..3b9c1dab 100644 --- a/content/openzeppelin-monitor/error.mdx +++ b/content/openzeppelin-monitor/error.mdx @@ -1,6 +1,5 @@ --- title: Error Handling -description: Error Handling --- ## Overview diff --git a/content/openzeppelin-monitor/index.mdx b/content/openzeppelin-monitor/index.mdx index 832a3be4..dcf14961 100644 --- a/content/openzeppelin-monitor/index.mdx +++ b/content/openzeppelin-monitor/index.mdx @@ -1,6 +1,5 @@ --- title: OpenZeppelin Monitor -description: OpenZeppelin Monitor --- ## Overview diff --git a/content/openzeppelin-monitor/project-structure.mdx b/content/openzeppelin-monitor/project-structure.mdx index aac6fd46..5be407f2 100644 --- a/content/openzeppelin-monitor/project-structure.mdx +++ b/content/openzeppelin-monitor/project-structure.mdx @@ -1,6 +1,5 @@ --- title: Project Structure -description: Project Structure --- This document describes the project structure and organization of the OpenZeppelin Monitor codebase, including the source code layout, configuration files, and development resources. diff --git a/content/openzeppelin-monitor/quickstart.mdx b/content/openzeppelin-monitor/quickstart.mdx index dc74f8a0..07c4a68e 100644 --- a/content/openzeppelin-monitor/quickstart.mdx +++ b/content/openzeppelin-monitor/quickstart.mdx @@ -1,6 +1,5 @@ --- title: Quick Start Guide -description: Quick Start Guide --- OpenZeppelin Monitor is a powerful tool for monitoring blockchain events and transactions. This guide will help you get up and running quickly with practical examples for both EVM and Stellar networks. diff --git a/content/openzeppelin-monitor/rpc.mdx b/content/openzeppelin-monitor/rpc.mdx index db302d5e..763d9580 100644 --- a/content/openzeppelin-monitor/rpc.mdx +++ b/content/openzeppelin-monitor/rpc.mdx @@ -1,6 +1,5 @@ --- title: RPC Client -description: RPC Client --- ## Overview diff --git a/content/openzeppelin-monitor/scripts.mdx b/content/openzeppelin-monitor/scripts.mdx index efbd260f..c2b4e826 100644 --- a/content/openzeppelin-monitor/scripts.mdx +++ b/content/openzeppelin-monitor/scripts.mdx @@ -1,6 +1,5 @@ --- title: Custom Scripts -description: Custom Scripts --- OpenZeppelin Monitor allows you to implement custom scripts for additional filtering of monitor matches and custom notification handling. diff --git a/content/openzeppelin-monitor/testing.mdx b/content/openzeppelin-monitor/testing.mdx index 16ef4a1b..7f9c5e27 100644 --- a/content/openzeppelin-monitor/testing.mdx +++ b/content/openzeppelin-monitor/testing.mdx @@ -1,6 +1,5 @@ --- title: Testing Guide -description: Testing Guide --- This document provides information about testing OpenZeppelin Monitor, including running tests, generating coverage reports, and understanding the test structure. diff --git a/content/openzeppelin-relayer/configuration/index.mdx b/content/openzeppelin-relayer/configuration/index.mdx index c04a74fe..678bdeca 100644 --- a/content/openzeppelin-relayer/configuration/index.mdx +++ b/content/openzeppelin-relayer/configuration/index.mdx @@ -1,6 +1,5 @@ --- title: Configuration -description: Configuration --- ## Overview diff --git a/content/openzeppelin-relayer/configuration/signers.mdx b/content/openzeppelin-relayer/configuration/signers.mdx index 830a2739..48b21037 100644 --- a/content/openzeppelin-relayer/configuration/signers.mdx +++ b/content/openzeppelin-relayer/configuration/signers.mdx @@ -1,6 +1,5 @@ --- title: Signers Configuration -description: Signers Configuration --- ## Overview diff --git a/content/openzeppelin-relayer/configuration/storage.mdx b/content/openzeppelin-relayer/configuration/storage.mdx index 29ebbfb6..6a471e8e 100644 --- a/content/openzeppelin-relayer/configuration/storage.mdx +++ b/content/openzeppelin-relayer/configuration/storage.mdx @@ -1,6 +1,5 @@ --- title: Storage Configuration -description: Storage Configuration --- ## Overview diff --git a/content/openzeppelin-relayer/evm.mdx b/content/openzeppelin-relayer/evm.mdx index c83bedc5..2f3e3029 100644 --- a/content/openzeppelin-relayer/evm.mdx +++ b/content/openzeppelin-relayer/evm.mdx @@ -1,6 +1,5 @@ --- title: EVM Integration -description: EVM Integration --- ## Overview diff --git a/content/openzeppelin-relayer/index.mdx b/content/openzeppelin-relayer/index.mdx index 5b6ac6f3..08aab46f 100644 --- a/content/openzeppelin-relayer/index.mdx +++ b/content/openzeppelin-relayer/index.mdx @@ -1,6 +1,5 @@ --- title: OpenZeppelin Relayer -description: OpenZeppelin Relayer --- ## Overview diff --git a/content/openzeppelin-relayer/network_configuration.mdx b/content/openzeppelin-relayer/network_configuration.mdx index 285ea4e2..ec05792a 100644 --- a/content/openzeppelin-relayer/network_configuration.mdx +++ b/content/openzeppelin-relayer/network_configuration.mdx @@ -1,6 +1,5 @@ --- title: Network Configuration -description: Network Configuration --- The OpenZeppelin Relayer supports multiple blockchain networks through a flexible JSON-based configuration system. This guide covers everything you need to know about configuring networks for your relayer instances. diff --git a/content/openzeppelin-relayer/plugins.mdx b/content/openzeppelin-relayer/plugins.mdx index 38a4db8f..7dce2335 100644 --- a/content/openzeppelin-relayer/plugins.mdx +++ b/content/openzeppelin-relayer/plugins.mdx @@ -1,6 +1,5 @@ --- title: Plugins -description: Plugins --- ## Overview diff --git a/content/openzeppelin-relayer/quickstart.mdx b/content/openzeppelin-relayer/quickstart.mdx index ab03f683..d8e69fd9 100644 --- a/content/openzeppelin-relayer/quickstart.mdx +++ b/content/openzeppelin-relayer/quickstart.mdx @@ -1,6 +1,5 @@ --- title: Quick Start Guide -description: Quick Start Guide --- This guide provides step-by-step instructions for setting up OpenZeppelin Relayer. It includes prerequisites, installation, and configuration examples. diff --git a/content/openzeppelin-relayer/roadmap.mdx b/content/openzeppelin-relayer/roadmap.mdx index ab8005d2..80dc0279 100644 --- a/content/openzeppelin-relayer/roadmap.mdx +++ b/content/openzeppelin-relayer/roadmap.mdx @@ -1,6 +1,5 @@ --- title: OpenZeppelin Relayer Roadmap -description: OpenZeppelin Relayer Roadmap --- This document outlines the planned development roadmap for the OpenZeppelin Relayer project. Please note that priorities and timelines may shift based on community feedback, security considerations, and emerging blockchain ecosystem needs. diff --git a/content/openzeppelin-relayer/solana.mdx b/content/openzeppelin-relayer/solana.mdx index 6d3ebd28..6d5fadba 100644 --- a/content/openzeppelin-relayer/solana.mdx +++ b/content/openzeppelin-relayer/solana.mdx @@ -1,6 +1,5 @@ --- title: Solana Integration -description: Solana Integration --- ## Overview diff --git a/content/openzeppelin-relayer/stellar.mdx b/content/openzeppelin-relayer/stellar.mdx index 09f26048..e267b6e1 100644 --- a/content/openzeppelin-relayer/stellar.mdx +++ b/content/openzeppelin-relayer/stellar.mdx @@ -1,6 +1,5 @@ --- title: Stellar Integration -description: Stellar Integration --- ## Overview diff --git a/content/openzeppelin-relayer/structure.mdx b/content/openzeppelin-relayer/structure.mdx index 7406fede..0704bb4a 100644 --- a/content/openzeppelin-relayer/structure.mdx +++ b/content/openzeppelin-relayer/structure.mdx @@ -1,6 +1,5 @@ --- title: Project Structure -description: Project Structure --- This document provides detailed information about each directory in the OpenZeppelin Relayer project. diff --git a/content/stellar-contracts/access-control.mdx b/content/stellar-contracts/access-control.mdx index 9db25800..027c4c31 100644 --- a/content/stellar-contracts/access-control.mdx +++ b/content/stellar-contracts/access-control.mdx @@ -1,6 +1,5 @@ --- title: Access Control -description: Access Control --- [Source Code](https://github.com/OpenZeppelin/stellar-contracts/tree/main/packages/access/src/access-control) diff --git a/content/stellar-contracts/get-started.mdx b/content/stellar-contracts/get-started.mdx index fb881ce9..e7a29948 100644 --- a/content/stellar-contracts/get-started.mdx +++ b/content/stellar-contracts/get-started.mdx @@ -1,6 +1,5 @@ --- title: Get Started -description: Get Started --- Not sure where to start? Use the interactive generator below to bootstrap your contract and find about the components offered in OpenZeppelin Smart Contracts Suite for Stellar. You can also access the code generator from [here](https://wizard.openzeppelin.com/stellar). diff --git a/content/stellar-contracts/helpers/default-impl-macro.mdx b/content/stellar-contracts/helpers/default-impl-macro.mdx index 26304ba4..b89659cb 100644 --- a/content/stellar-contracts/helpers/default-impl-macro.mdx +++ b/content/stellar-contracts/helpers/default-impl-macro.mdx @@ -1,6 +1,5 @@ --- title: Default Implementation Macro -description: Default Implementation Macro --- [Source Code](https://github.com/OpenZeppelin/stellar-contracts/tree/main/packages/contract-utils/src/default-impl-macro) diff --git a/content/stellar-contracts/index.mdx b/content/stellar-contracts/index.mdx index 476d4ecf..de3cf465 100644 --- a/content/stellar-contracts/index.mdx +++ b/content/stellar-contracts/index.mdx @@ -1,6 +1,5 @@ --- title: Stellar Smart Contracts Suite -description: Stellar Smart Contracts Suite --- A comprehensive collection of secure, scalable smart contracts and utilities for the Stellar network, diff --git a/content/stellar-contracts/ownable.mdx b/content/stellar-contracts/ownable.mdx index fa4fb8d4..8de0ce36 100644 --- a/content/stellar-contracts/ownable.mdx +++ b/content/stellar-contracts/ownable.mdx @@ -1,6 +1,5 @@ --- title: Ownable -description: Ownable --- [Source Code](https://github.com/OpenZeppelin/stellar-contracts/tree/main/packages/access/src/ownable) diff --git a/content/stellar-contracts/tokens/fungible/fungible.mdx b/content/stellar-contracts/tokens/fungible/fungible.mdx index f535cce9..991bdca1 100644 --- a/content/stellar-contracts/tokens/fungible/fungible.mdx +++ b/content/stellar-contracts/tokens/fungible/fungible.mdx @@ -1,6 +1,5 @@ --- title: Fungible Token -description: Fungible Token --- [Source Code](https://github.com/OpenZeppelin/stellar-contracts/tree/main/packages/tokens/src/fungible) diff --git a/content/stellar-contracts/tokens/fungible/sac-admin-generic.mdx b/content/stellar-contracts/tokens/fungible/sac-admin-generic.mdx index e13d2836..bc35c147 100644 --- a/content/stellar-contracts/tokens/fungible/sac-admin-generic.mdx +++ b/content/stellar-contracts/tokens/fungible/sac-admin-generic.mdx @@ -1,6 +1,5 @@ --- title: SAC Admin Generic -description: SAC Admin Generic --- [Source Code](https://github.com/OpenZeppelin/stellar-contracts/tree/main/packages/tokens/src/fungible/utils/sac_admin_generic) diff --git a/content/stellar-contracts/tokens/fungible/sac-admin-wrapper.mdx b/content/stellar-contracts/tokens/fungible/sac-admin-wrapper.mdx index 48acf222..6bf593dc 100644 --- a/content/stellar-contracts/tokens/fungible/sac-admin-wrapper.mdx +++ b/content/stellar-contracts/tokens/fungible/sac-admin-wrapper.mdx @@ -1,6 +1,5 @@ --- title: SAC Admin Wrapper -description: SAC Admin Wrapper --- [Source Code](https://github.com/OpenZeppelin/stellar-contracts/tree/main/packages/tokens/src/fungible/utils/sac_admin_wrapper) diff --git a/content/stellar-contracts/tokens/non-fungible/nft-consecutive.mdx b/content/stellar-contracts/tokens/non-fungible/nft-consecutive.mdx index 606d01dd..f977f4e3 100644 --- a/content/stellar-contracts/tokens/non-fungible/nft-consecutive.mdx +++ b/content/stellar-contracts/tokens/non-fungible/nft-consecutive.mdx @@ -1,6 +1,5 @@ --- title: Non-Fungible Consecutive -description: Non-Fungible Consecutive --- [Source Code](https://github.com/OpenZeppelin/stellar-contracts/tree/main/packages/tokens/src/non-fungible/extensions/consecutive) diff --git a/content/stellar-contracts/tokens/non-fungible/nft-enumerable.mdx b/content/stellar-contracts/tokens/non-fungible/nft-enumerable.mdx index 121547cf..eb4fd57f 100644 --- a/content/stellar-contracts/tokens/non-fungible/nft-enumerable.mdx +++ b/content/stellar-contracts/tokens/non-fungible/nft-enumerable.mdx @@ -1,6 +1,5 @@ --- title: Non-Fungible Enumerable -description: Non-Fungible Enumerable --- [Source Code](https://github.com/OpenZeppelin/stellar-contracts/tree/main/packages/tokens/src/non-fungible/extensions/enumerable) diff --git a/content/stellar-contracts/tokens/non-fungible/non-fungible.mdx b/content/stellar-contracts/tokens/non-fungible/non-fungible.mdx index 1f1b66f3..ba43c1df 100644 --- a/content/stellar-contracts/tokens/non-fungible/non-fungible.mdx +++ b/content/stellar-contracts/tokens/non-fungible/non-fungible.mdx @@ -1,6 +1,5 @@ --- title: Non-Fungible Token -description: Non-Fungible Token --- [Source Code](https://github.com/OpenZeppelin/stellar-contracts/tree/main/packages/tokens/src/non-fungible) diff --git a/content/stellar-contracts/utils/crypto.mdx b/content/stellar-contracts/utils/crypto.mdx index 435a3d29..b50af954 100644 --- a/content/stellar-contracts/utils/crypto.mdx +++ b/content/stellar-contracts/utils/crypto.mdx @@ -1,6 +1,5 @@ --- title: Cryptography Utilities -description: Cryptography Utilities --- [Crypto Source Code](https://github.com/OpenZeppelin/stellar-contracts/tree/main/packages/contract-utils/src/crypto) | diff --git a/content/stellar-contracts/utils/pausable.mdx b/content/stellar-contracts/utils/pausable.mdx index 8247e7f6..a462ba09 100644 --- a/content/stellar-contracts/utils/pausable.mdx +++ b/content/stellar-contracts/utils/pausable.mdx @@ -1,6 +1,5 @@ --- title: Pausable -description: Pausable --- [Source Code](https://github.com/OpenZeppelin/stellar-contracts/tree/main/packages/contract-utils/src/pausable) diff --git a/content/stellar-contracts/utils/upgradeable.mdx b/content/stellar-contracts/utils/upgradeable.mdx index 66ff1873..7c6cb452 100644 --- a/content/stellar-contracts/utils/upgradeable.mdx +++ b/content/stellar-contracts/utils/upgradeable.mdx @@ -1,6 +1,5 @@ --- title: Upgrades and Migrations -description: Upgrades and Migrations --- [Source code](https://github.com/OpenZeppelin/stellar-contracts/tree/main/packages/contract-utils/src/upgradeable) diff --git a/content/substrate-runtimes/glossary.mdx b/content/substrate-runtimes/glossary.mdx index ba5225fd..ee01b857 100644 --- a/content/substrate-runtimes/glossary.mdx +++ b/content/substrate-runtimes/glossary.mdx @@ -1,6 +1,5 @@ --- title: Glossary -description: Glossary --- ## A diff --git a/content/substrate-runtimes/guides/async_backing.mdx b/content/substrate-runtimes/guides/async_backing.mdx index 14038ab9..9248a1ec 100644 --- a/content/substrate-runtimes/guides/async_backing.mdx +++ b/content/substrate-runtimes/guides/async_backing.mdx @@ -1,6 +1,5 @@ --- title: Async Backing -description: Async Backing --- Async backing is a feature of parachains that allow to shorten the block time, get more execution time and speed up transaction inclusion in general. You can read in details what async backing is in the [general guide](https://wiki.polkadot.network/docs/learn-async-backing#candidate-receipt) and in the [update guide](https://wiki.polkadot.network/docs/maintain-guides-async-backing). In our generic template we have included async backing under a feature. This page describes how can you build and test the template with async backing enabled. diff --git a/content/substrate-runtimes/guides/contract_migration.mdx b/content/substrate-runtimes/guides/contract_migration.mdx index 94bcea4e..71373d0c 100644 --- a/content/substrate-runtimes/guides/contract_migration.mdx +++ b/content/substrate-runtimes/guides/contract_migration.mdx @@ -1,6 +1,5 @@ --- title: Migrate a Contract from EVM chain -description: Migrate a Contract from EVM chain --- This EVM template is powered by Frontier. It allows you to deploy almost any Solidity contract you have following these steps. diff --git a/content/substrate-runtimes/guides/hrmp_channels.mdx b/content/substrate-runtimes/guides/hrmp_channels.mdx index 15675757..1fd1c676 100644 --- a/content/substrate-runtimes/guides/hrmp_channels.mdx +++ b/content/substrate-runtimes/guides/hrmp_channels.mdx @@ -1,6 +1,5 @@ --- title: Sending Cross-Chain Messages between Parachains -description: Sending Cross-Chain Messages between Parachains --- The supported way to exchange cross-chain messages (XCM) between parachains is to use Horizontal Relay-routed Message Passing (HRMP) channels. diff --git a/content/substrate-runtimes/guides/pallet_abstractions.mdx b/content/substrate-runtimes/guides/pallet_abstractions.mdx index 0db6c6fd..b3214b22 100644 --- a/content/substrate-runtimes/guides/pallet_abstractions.mdx +++ b/content/substrate-runtimes/guides/pallet_abstractions.mdx @@ -1,6 +1,5 @@ --- title: OpenZeppelin Pallet Abstractions -description: OpenZeppelin Pallet Abstractions --- ### Introduction diff --git a/content/substrate-runtimes/guides/pay_dot_as_a_fee.mdx b/content/substrate-runtimes/guides/pay_dot_as_a_fee.mdx index 9ace1fbf..1bf9be6a 100644 --- a/content/substrate-runtimes/guides/pay_dot_as_a_fee.mdx +++ b/content/substrate-runtimes/guides/pay_dot_as_a_fee.mdx @@ -1,6 +1,5 @@ --- title: Pay DOT as a Fee -description: Pay DOT as a Fee --- This feature allows you to set DOT (or any other registered asset) as a fee for transaction execution. Here are the steps that you will need to execute to support it. diff --git a/content/substrate-runtimes/guides/predeployed_contracts.mdx b/content/substrate-runtimes/guides/predeployed_contracts.mdx index 1d74cfd2..6ed1414f 100644 --- a/content/substrate-runtimes/guides/predeployed_contracts.mdx +++ b/content/substrate-runtimes/guides/predeployed_contracts.mdx @@ -1,6 +1,5 @@ --- title: Predeployed Contracts -description: Predeployed Contracts --- To enable some bleeding-edge features that EVM provides (like pay gas with any tokens), we have developed an ability to deploy contracts in genesis config. Initially it was done to deploy Entrypoint contract only, but we have supported this functionality for any contract with some limitations. diff --git a/content/substrate-runtimes/guides/quick_start.mdx b/content/substrate-runtimes/guides/quick_start.mdx index 428e5d5f..cebd711c 100644 --- a/content/substrate-runtimes/guides/quick_start.mdx +++ b/content/substrate-runtimes/guides/quick_start.mdx @@ -1,6 +1,5 @@ --- title: Quick start -description: Quick start --- * Begin by visiting our [repository](https://github.com/OpenZeppelin/polkadot-runtime-templates). You can fork it, or simply clone it to your local directory. diff --git a/content/substrate-runtimes/guides/rpc_differences.mdx b/content/substrate-runtimes/guides/rpc_differences.mdx index d110fb2f..954c974b 100644 --- a/content/substrate-runtimes/guides/rpc_differences.mdx +++ b/content/substrate-runtimes/guides/rpc_differences.mdx @@ -1,6 +1,5 @@ --- title: RPC Differences -description: RPC Differences --- The EVM in a Substrate node has almost the same RPC calls common to all nodes. But some of them are not applicable for Frontier. diff --git a/content/substrate-runtimes/guides/testing_with_zombienet.mdx b/content/substrate-runtimes/guides/testing_with_zombienet.mdx index 5dab639f..57faecea 100644 --- a/content/substrate-runtimes/guides/testing_with_zombienet.mdx +++ b/content/substrate-runtimes/guides/testing_with_zombienet.mdx @@ -1,6 +1,5 @@ --- title: Testing Solidity Smart Contracts with Zombienet -description: Testing Solidity Smart Contracts with Zombienet --- In this tutorial, we will demonstrate how to deploy your parachain using Zombienet, and test the functionalities of your parachain. diff --git a/content/substrate-runtimes/guides/weights_fees.mdx b/content/substrate-runtimes/guides/weights_fees.mdx index 42769699..e510e888 100644 --- a/content/substrate-runtimes/guides/weights_fees.mdx +++ b/content/substrate-runtimes/guides/weights_fees.mdx @@ -1,6 +1,5 @@ --- title: Weights & Fees -description: Weights & Fees --- Weight is a metric to estimate the time it takes to execute code. diff --git a/content/substrate-runtimes/index.mdx b/content/substrate-runtimes/index.mdx index d9c9be8e..e75fa08a 100644 --- a/content/substrate-runtimes/index.mdx +++ b/content/substrate-runtimes/index.mdx @@ -1,6 +1,5 @@ --- title: Polkadot Parachain Runtimes -description: Polkadot Parachain Runtimes --- A collection of secure runtime templates to build parachains more easily on Polkadot. diff --git a/content/substrate-runtimes/misc/multisig-accounts.mdx b/content/substrate-runtimes/misc/multisig-accounts.mdx index cc5b8b82..ce1026df 100644 --- a/content/substrate-runtimes/misc/multisig-accounts.mdx +++ b/content/substrate-runtimes/misc/multisig-accounts.mdx @@ -1,6 +1,5 @@ --- title: Multisig Accounts -description: Multisig Accounts --- * Multisig accounts do not have a private key. They are managed by their signatories. diff --git a/content/substrate-runtimes/pallets/assets.mdx b/content/substrate-runtimes/pallets/assets.mdx index 60de2bd7..3c0e0f0c 100644 --- a/content/substrate-runtimes/pallets/assets.mdx +++ b/content/substrate-runtimes/pallets/assets.mdx @@ -1,6 +1,5 @@ --- title: pallet_assets -description: pallet_assets --- Branch/Release: `release-polkadot-v1.10.0` diff --git a/content/substrate-runtimes/pallets/aura_ext.mdx b/content/substrate-runtimes/pallets/aura_ext.mdx index 20749a91..bde84be1 100644 --- a/content/substrate-runtimes/pallets/aura_ext.mdx +++ b/content/substrate-runtimes/pallets/aura_ext.mdx @@ -1,6 +1,5 @@ --- title: cumulus_pallet_aura_ext -description: cumulus_pallet_aura_ext --- Branch/Release: `release-polkadot-v1.10.0` diff --git a/content/substrate-runtimes/pallets/balances.mdx b/content/substrate-runtimes/pallets/balances.mdx index 3969dee6..21a6b878 100644 --- a/content/substrate-runtimes/pallets/balances.mdx +++ b/content/substrate-runtimes/pallets/balances.mdx @@ -1,6 +1,5 @@ --- title: pallet_balances -description: pallet_balances --- Branch/Release: `release-polkadot-v1.10.0` diff --git a/content/substrate-runtimes/pallets/collator-selection.mdx b/content/substrate-runtimes/pallets/collator-selection.mdx index 63fd4283..13004a14 100644 --- a/content/substrate-runtimes/pallets/collator-selection.mdx +++ b/content/substrate-runtimes/pallets/collator-selection.mdx @@ -1,6 +1,5 @@ --- title: collator_selection -description: collator_selection --- Branch/Release: `release-polkadot-v1.10.0` diff --git a/content/substrate-runtimes/pallets/message-queue.mdx b/content/substrate-runtimes/pallets/message-queue.mdx index dd3f15de..1c604b87 100644 --- a/content/substrate-runtimes/pallets/message-queue.mdx +++ b/content/substrate-runtimes/pallets/message-queue.mdx @@ -1,6 +1,5 @@ --- title: pallet_message_queue -description: pallet_message_queue --- Branch/Release: `release-polkadot-v1.10.0` diff --git a/content/substrate-runtimes/pallets/multisig.mdx b/content/substrate-runtimes/pallets/multisig.mdx index 48d48560..2d9bfe92 100644 --- a/content/substrate-runtimes/pallets/multisig.mdx +++ b/content/substrate-runtimes/pallets/multisig.mdx @@ -1,6 +1,5 @@ --- title: pallet_multisig -description: pallet_multisig --- Branch/Release: `release-polkadot-v1.10.0` diff --git a/content/substrate-runtimes/pallets/parachain-system.mdx b/content/substrate-runtimes/pallets/parachain-system.mdx index ebcda420..f2724360 100644 --- a/content/substrate-runtimes/pallets/parachain-system.mdx +++ b/content/substrate-runtimes/pallets/parachain-system.mdx @@ -1,6 +1,5 @@ --- title: parachain_system -description: parachain_system --- Branch/Release: `release-polkadot-v1.10.0` diff --git a/content/substrate-runtimes/pallets/proxy.mdx b/content/substrate-runtimes/pallets/proxy.mdx index f5005c7b..b2f98b4e 100644 --- a/content/substrate-runtimes/pallets/proxy.mdx +++ b/content/substrate-runtimes/pallets/proxy.mdx @@ -1,6 +1,5 @@ --- title: pallet_proxy -description: pallet_proxy --- Branch/Release: `release-polkadot-v1.10.0` diff --git a/content/substrate-runtimes/pallets/transaction_payment.mdx b/content/substrate-runtimes/pallets/transaction_payment.mdx index f50fee0a..dca449cf 100644 --- a/content/substrate-runtimes/pallets/transaction_payment.mdx +++ b/content/substrate-runtimes/pallets/transaction_payment.mdx @@ -1,6 +1,5 @@ --- title: pallet_transaction_payment -description: pallet_transaction_payment --- Branch/Release: `release-polkadot-v1.10.0` diff --git a/content/substrate-runtimes/pallets/treasury.mdx b/content/substrate-runtimes/pallets/treasury.mdx index ecd1a65b..e2a75cc1 100644 --- a/content/substrate-runtimes/pallets/treasury.mdx +++ b/content/substrate-runtimes/pallets/treasury.mdx @@ -1,6 +1,5 @@ --- title: pallet_treasury -description: pallet_treasury --- Branch/Release: `release-polkadot-v1.10.0` diff --git a/content/substrate-runtimes/pallets/xcm.mdx b/content/substrate-runtimes/pallets/xcm.mdx index a9279570..dcce14a8 100644 --- a/content/substrate-runtimes/pallets/xcm.mdx +++ b/content/substrate-runtimes/pallets/xcm.mdx @@ -1,6 +1,5 @@ --- title: pallet_xcm -description: pallet_xcm --- Branch/Release: `release-polkadot-v1.10.0` diff --git a/content/substrate-runtimes/pallets/xcmp-queue.mdx b/content/substrate-runtimes/pallets/xcmp-queue.mdx index 0bb6e036..a9a77499 100644 --- a/content/substrate-runtimes/pallets/xcmp-queue.mdx +++ b/content/substrate-runtimes/pallets/xcmp-queue.mdx @@ -1,6 +1,5 @@ --- title: cumulus_pallet_xcmp_queue -description: cumulus_pallet_xcmp_queue --- Branch/Release: `release-polkadot-v1.10.0` diff --git a/content/substrate-runtimes/runtime/xcm_executor.mdx b/content/substrate-runtimes/runtime/xcm_executor.mdx index 3a62d9b3..eaef7487 100644 --- a/content/substrate-runtimes/runtime/xcm_executor.mdx +++ b/content/substrate-runtimes/runtime/xcm_executor.mdx @@ -1,6 +1,5 @@ --- title: XCM Executor -description: XCM Executor --- Branch/Release: `release-polkadot-v1.10.0` diff --git a/content/substrate-runtimes/runtimes/evm.mdx b/content/substrate-runtimes/runtimes/evm.mdx index 2f840adc..4fec5095 100644 --- a/content/substrate-runtimes/runtimes/evm.mdx +++ b/content/substrate-runtimes/runtimes/evm.mdx @@ -1,6 +1,5 @@ --- title: EVM Runtime -description: EVM Runtime --- ## Purpose diff --git a/content/substrate-runtimes/runtimes/generic.mdx b/content/substrate-runtimes/runtimes/generic.mdx index 3a74c64c..351d8cae 100644 --- a/content/substrate-runtimes/runtimes/generic.mdx +++ b/content/substrate-runtimes/runtimes/generic.mdx @@ -1,6 +1,5 @@ --- title: Generic Runtime -description: Generic Runtime --- ## Purpose diff --git a/content/uniswap-hooks/base.mdx b/content/uniswap-hooks/base.mdx index 8f123ad9..70917455 100644 --- a/content/uniswap-hooks/base.mdx +++ b/content/uniswap-hooks/base.mdx @@ -1,6 +1,5 @@ --- title: Base -description: Base --- Base contract implementations are provided in the library as building blocks to leverage Uniswap v4’s features natively, such as custom accounting, custom curves, and asynchronous swaps. diff --git a/content/uniswap-hooks/fee.mdx b/content/uniswap-hooks/fee.mdx index d06a9c17..6e953c9e 100644 --- a/content/uniswap-hooks/fee.mdx +++ b/content/uniswap-hooks/fee.mdx @@ -1,6 +1,5 @@ --- title: Fee -description: Fee --- Fee-related base hooks are provided in the library to allow for flexible ways to adjust or override fees for different actions, like swaps and liquidity modifications. diff --git a/content/uniswap-hooks/index.mdx b/content/uniswap-hooks/index.mdx index 5a1fe681..b8673145 100644 --- a/content/uniswap-hooks/index.mdx +++ b/content/uniswap-hooks/index.mdx @@ -1,6 +1,5 @@ --- title: Uniswap Hooks -description: Uniswap Hooks --- A [Solidity library](https://github.com/OpenZeppelin/uniswap-hooks) for secure and modular hooks for [Uniswap v4](https://docs.uniswap.org/contracts/v4/overview). This library includes: diff --git a/content/uniswap-hooks/utilities.mdx b/content/uniswap-hooks/utilities.mdx index 33e473bb..f40f7b01 100644 --- a/content/uniswap-hooks/utilities.mdx +++ b/content/uniswap-hooks/utilities.mdx @@ -1,6 +1,5 @@ --- title: Utilities -description: Utilities --- Libraries and general purpose utilities are included in the library to help develop hooks. For technical details, refer to the [API Reference](contracts/v5.x/api/utils.mdx). diff --git a/content/upgrade-plugins/api-core.mdx b/content/upgrade-plugins/api-core.mdx index f4a61f64..acaf72c7 100644 --- a/content/upgrade-plugins/api-core.mdx +++ b/content/upgrade-plugins/api-core.mdx @@ -1,6 +1,5 @@ --- title: OpenZeppelin Upgrades Core & CLI -description: OpenZeppelin Upgrades Core & CLI --- The `@openzeppelin/upgrades-core` package provides a `validate` command to check for upgrade safety and storage layout compatibility in upgradeable contracts. It can be used throughout your development process to ensure that your contracts are upgrade safe and compatible with previous versions. diff --git a/content/upgrade-plugins/api-hardhat-upgrades.mdx b/content/upgrade-plugins/api-hardhat-upgrades.mdx index 1c9bf145..558bc5ce 100644 --- a/content/upgrade-plugins/api-hardhat-upgrades.mdx +++ b/content/upgrade-plugins/api-hardhat-upgrades.mdx @@ -1,6 +1,5 @@ --- title: OpenZeppelin Hardhat Upgrades API -description: OpenZeppelin Hardhat Upgrades API --- Both `deployProxy` and `upgradeProxy` functions will return instances of [ethers.js contracts](https://docs.ethers.io/v5/api/contract/contract), and require [ethers.js contract factories](https://docs.ethers.io/v5/api/contract/contract-factory) as arguments. For [beacons](https://docs.openzeppelin.com/contracts/api/proxy#beacon), `deployBeacon` and `upgradeBeacon` will both return an upgradable beacon instance that can be used with a beacon proxy. All deploy and upgrade functions validate that the implementation contract is upgrade-safe, and will fail otherwise. diff --git a/content/upgrade-plugins/api-truffle-upgrades.mdx b/content/upgrade-plugins/api-truffle-upgrades.mdx index 6cda80fe..a874c90c 100644 --- a/content/upgrade-plugins/api-truffle-upgrades.mdx +++ b/content/upgrade-plugins/api-truffle-upgrades.mdx @@ -1,6 +1,5 @@ --- title: OpenZeppelin Truffle Upgrades API -description: OpenZeppelin Truffle Upgrades API --- diff --git a/content/upgrade-plugins/defender-deploy.mdx b/content/upgrade-plugins/defender-deploy.mdx index 1563b7bb..26e13af0 100644 --- a/content/upgrade-plugins/defender-deploy.mdx +++ b/content/upgrade-plugins/defender-deploy.mdx @@ -1,6 +1,5 @@ --- title: OpenZeppelin Defender with Hardhat -description: OpenZeppelin Defender with Hardhat --- The Hardhat Upgrades package can use [OpenZeppelin Defender](https://docs.openzeppelin.com/defender/) for deployments instead of ethers.js, which allows for features such as gas pricing estimation, resubmissions, and automated bytecode and source code verification. diff --git a/content/upgrade-plugins/faq.mdx b/content/upgrade-plugins/faq.mdx index 7731c8a4..344f7292 100644 --- a/content/upgrade-plugins/faq.mdx +++ b/content/upgrade-plugins/faq.mdx @@ -1,6 +1,5 @@ --- title: Frequently Asked Questions -description: Frequently Asked Questions --- ## Can I change Solidity compiler versions when upgrading? diff --git a/content/upgrade-plugins/foundry-defender.mdx b/content/upgrade-plugins/foundry-defender.mdx index 0d66f6d3..2cde3ce3 100644 --- a/content/upgrade-plugins/foundry-defender.mdx +++ b/content/upgrade-plugins/foundry-defender.mdx @@ -1,5 +1,4 @@ --- title: foundry-defender -description: foundry-defender --- diff --git a/content/upgrade-plugins/foundry/api/api-foundry-upgrades.mdx b/content/upgrade-plugins/foundry/api/api-foundry-upgrades.mdx index 283dd2e8..ec7ee9fe 100644 --- a/content/upgrade-plugins/foundry/api/api-foundry-upgrades.mdx +++ b/content/upgrade-plugins/foundry/api/api-foundry-upgrades.mdx @@ -1,6 +1,5 @@ --- title: OpenZeppelin Foundry Upgrades API -description: OpenZeppelin Foundry Upgrades API --- ## Contract name formats diff --git a/content/upgrade-plugins/foundry/foundry-defender.mdx b/content/upgrade-plugins/foundry/foundry-defender.mdx index 2968d248..02798c88 100644 --- a/content/upgrade-plugins/foundry/foundry-defender.mdx +++ b/content/upgrade-plugins/foundry/foundry-defender.mdx @@ -1,6 +1,5 @@ --- title: OpenZeppelin Defender with Foundry -description: OpenZeppelin Defender with Foundry --- OpenZeppelin Foundry Upgrades can be used for performing deployments through [OpenZeppelin Defender](https://docs.openzeppelin.com/defender/), which allows for features such as gas pricing estimation, resubmissions, and automated bytecode and source code verification. diff --git a/content/upgrade-plugins/foundry/foundry-upgrades.mdx b/content/upgrade-plugins/foundry/foundry-upgrades.mdx index ba7d20b6..d4b27b36 100644 --- a/content/upgrade-plugins/foundry/foundry-upgrades.mdx +++ b/content/upgrade-plugins/foundry/foundry-upgrades.mdx @@ -1,6 +1,5 @@ --- title: Using with Foundry -description: Using with Foundry --- Foundry library for deploying and managing upgradeable contracts, which includes upgrade safety validations. diff --git a/content/upgrade-plugins/hardhat-upgrades.mdx b/content/upgrade-plugins/hardhat-upgrades.mdx index b11e6964..5813ee77 100644 --- a/content/upgrade-plugins/hardhat-upgrades.mdx +++ b/content/upgrade-plugins/hardhat-upgrades.mdx @@ -1,6 +1,5 @@ --- title: Using with Hardhat -description: Using with Hardhat --- This package adds functions to your Hardhat scripts so you can deploy and upgrade proxies for your contracts. Depends on `ethers.js`. diff --git a/content/upgrade-plugins/index.mdx b/content/upgrade-plugins/index.mdx index 0cb59669..f0764656 100644 --- a/content/upgrade-plugins/index.mdx +++ b/content/upgrade-plugins/index.mdx @@ -1,6 +1,5 @@ --- title: Upgrades Plugins -description: Upgrades Plugins --- ***Integrate upgrades into your existing workflow.*** Plugins for [Hardhat](https://hardhat.org) and [Foundry](https://book.getfoundry.sh/) to deploy and manage upgradeable contracts on Ethereum. diff --git a/content/upgrade-plugins/migrate-from-cli.mdx b/content/upgrade-plugins/migrate-from-cli.mdx index 966ac4ba..5146049c 100644 --- a/content/upgrade-plugins/migrate-from-cli.mdx +++ b/content/upgrade-plugins/migrate-from-cli.mdx @@ -1,6 +1,5 @@ --- title: Migrate from OpenZeppelin CLI -description: Migrate from OpenZeppelin CLI --- diff --git a/content/upgrade-plugins/network-files.mdx b/content/upgrade-plugins/network-files.mdx index a7ac508e..e94b12e2 100644 --- a/content/upgrade-plugins/network-files.mdx +++ b/content/upgrade-plugins/network-files.mdx @@ -1,6 +1,5 @@ --- title: Network Files -description: Network Files --- OpenZeppelin Hardhat Upgrades, by default, keeps track of all the contract versions you have deployed in an `.openzeppelin` folder in the project root. You will find one file per network there. It is advised that you commit to source control the files for all networks except the development ones (you may see them as `.openzeppelin/unknown-*.json`). diff --git a/content/upgrade-plugins/proxies.mdx b/content/upgrade-plugins/proxies.mdx index 2f8fa386..9cd4360c 100644 --- a/content/upgrade-plugins/proxies.mdx +++ b/content/upgrade-plugins/proxies.mdx @@ -1,6 +1,5 @@ --- title: Proxy Upgrade Pattern -description: Proxy Upgrade Pattern --- This article describes the "unstructured storage" proxy pattern, the fundamental building block of OpenZeppelin Upgrades. diff --git a/content/upgrade-plugins/truffle-upgrades.mdx b/content/upgrade-plugins/truffle-upgrades.mdx index 1eb780b9..9d01f392 100644 --- a/content/upgrade-plugins/truffle-upgrades.mdx +++ b/content/upgrade-plugins/truffle-upgrades.mdx @@ -1,6 +1,5 @@ --- title: Using with Truffle -description: Using with Truffle --- diff --git a/content/upgrade-plugins/writing-upgradeable.mdx b/content/upgrade-plugins/writing-upgradeable.mdx index b79b88f9..efd7811f 100644 --- a/content/upgrade-plugins/writing-upgradeable.mdx +++ b/content/upgrade-plugins/writing-upgradeable.mdx @@ -1,6 +1,5 @@ --- title: Writing Upgradeable Contracts -description: Writing Upgradeable Contracts --- When working with upgradeable contracts using OpenZeppelin Upgrades, there are a few minor caveats to keep in mind when writing your Solidity code. From 978e80f94c457d1a7912052706e65fbc84985d6e Mon Sep 17 00:00:00 2001 From: Steve Date: Mon, 8 Sep 2025 13:16:09 -0400 Subject: [PATCH 02/14] chore: updated stellar to v4 release --- .../{ => access}/access-control.mdx | 36 +++---- content/stellar-contracts/access/ownable.mdx | 99 +++++++++++++++++++ content/stellar-contracts/changelog.mdx | 4 +- .../helpers/default-impl-macro.mdx | 14 +-- content/stellar-contracts/index.mdx | 10 +- content/stellar-contracts/ownable.mdx | 6 +- .../tokens/fungible/fungible.mdx | 30 +++--- .../tokens/fungible/sac-admin-generic.mdx | 90 ++++++++--------- .../tokens/fungible/sac-admin-wrapper.mdx | 36 +++---- .../tokens/non-fungible/nft-consecutive.mdx | 34 +++---- .../tokens/non-fungible/nft-enumerable.mdx | 34 +++---- .../tokens/non-fungible/non-fungible.mdx | 32 +++--- content/stellar-contracts/utils/crypto.mdx | 56 +++++------ content/stellar-contracts/utils/pausable.mdx | 8 +- .../stellar-contracts/utils/upgradeable.mdx | 74 +++++++------- src/navigation/stellar.json | 59 +++++++++-- 16 files changed, 382 insertions(+), 240 deletions(-) rename content/stellar-contracts/{ => access}/access-control.mdx (87%) create mode 100644 content/stellar-contracts/access/ownable.mdx diff --git a/content/stellar-contracts/access-control.mdx b/content/stellar-contracts/access/access-control.mdx similarity index 87% rename from content/stellar-contracts/access-control.mdx rename to content/stellar-contracts/access/access-control.mdx index 027c4c31..06682f86 100644 --- a/content/stellar-contracts/access-control.mdx +++ b/content/stellar-contracts/access/access-control.mdx @@ -46,48 +46,48 @@ Roles exist only through their relationships with accounts, so a role with zero Here’s a simple example of using the Access Control module: ```rust -use soroban_sdk::{contract, contractimpl, symbol_short, Address, Env}; -use stellar_access::access_control::{self as access_control, AccessControl}; -use stellar_macros::{has_role, only_admin}; +use soroban_sdk::contract, contractimpl, symbol_short, Address, Env; +use stellar_access::access_control::self as access_control, AccessControl; +use stellar_macros::has_role, only_admin; #[contract] pub struct MyContract; #[contractimpl] -impl MyContract { - pub fn __constructor(e: &Env, admin: Address) { +impl MyContract + pub fn __constructor(e: &Env, admin: Address) // Set the contract admin access_control::set_admin(e, &admin); // Create a "minter" role with admin as its admin access_control::set_role_admin_no_auth(e, &symbol_short!("minter"), &symbol_short!("admin")); - } + #[only_admin] - pub fn admin_restricted_function(e: &Env) -> Vec { + pub fn admin_restricted_function(e: &Env) -> Vec vec![&e, String::from_str(e, "seems sus")] - } + // we want `require_auth()` provided by the macro, since there is no // `require_auth()` in `Base::mint`. #[only_role(caller, "minter")] - pub fn mint(e: &Env, caller: Address, to: Address, token_id: u32) { + pub fn mint(e: &Env, caller: Address, to: Address, token_id: u32) Base::mint(e, &to, token_id) - } + // allows either minter or burner role, does not enforce `require_auth` in the macro #[has_any_role(caller, ["minter", "burner"])] - pub fn multi_role_action(e: &Env, caller: Address) -> String { + pub fn multi_role_action(e: &Env, caller: Address) -> String caller.require_auth(); String::from_str(e, "multi_role_action_success") - } + // allows either minter or burner role AND enforces `require_auth` in the macro #[only_any_role(caller, ["minter", "burner"])] - pub fn multi_role_auth_action(e: &Env, caller: Address) -> String { + pub fn multi_role_auth_action(e: &Env, caller: Address) -> String String::from_str(e, "multi_role_auth_action_success") - } -} + + ``` ## Benefits and Trade-offs @@ -106,6 +106,6 @@ impl MyContract { ## See Also -* [Ownable](access/ownable.adoc) -* [Fungible Token](tokens/fungible/fungible.adoc) -* [Non-Fungible Token](tokens/non-fungible/non-fungible.adoc) +* [Ownable](/stellar-contracts/access/ownable) +* [Fungible Token](/stellar-contracts/tokens/fungible/fungible) +* [Non-Fungible Token](/stellar-contracts/tokens/non-fungible/non-fungible) diff --git a/content/stellar-contracts/access/ownable.mdx b/content/stellar-contracts/access/ownable.mdx new file mode 100644 index 00000000..b713e700 --- /dev/null +++ b/content/stellar-contracts/access/ownable.mdx @@ -0,0 +1,99 @@ +--- +title: Ownable +--- + +[Source Code](https://github.com/OpenZeppelin/stellar-contracts/tree/main/packages/access/src/ownable) + +## Overview + +The Ownable module provides a simple access control mechanism where a contract has a single account (owner) that can be granted exclusive access to specific functions. This pattern is useful for contracts that need a straightforward authorization system with a single privileged account. + +## Key Concepts + +### Ownership Management + +The system designates a single owner with exclusive access to functions marked with the `#[only_owner]` macro. The initial owner must be ideally set during contract initialization for the module to function properly. + +Like the Access Control module, ownership transfers are implemented as a two-step process to prevent accidental or malicious takeovers: + +1. The current owner **initiates** the transfer by specifying the new owner and an expiration time (`live_until_ledger`). +2. The designated new owner must **explicitly accept** the transfer to complete it. + +Until the transfer is accepted, the original owner retains full control and can override or cancel the transfer by initiating a new one or using a `live_until_ledger` of `0`. + +### Ownership Renunciation + +The Ownable module allows the owner to permanently renounce ownership of the contract. This is a one-way operation that cannot be undone. After ownership is renounced, all functions marked with `#[only_owner]` become permanently inaccessible. + +This feature is useful for contracts that need to become fully decentralized after an initial setup phase. + +### Procedural Macro + +The module includes a procedural macro to simplify owner authorization checks: + +#### @only_owner + +Ensures the caller is the owner before executing the function: + +```rust +#[only_owner] +pub fn restricted_function(e: &Env, other_param: u32) + // Function body - only accessible to owner + +``` + +This expands to code that retrieves the owner from storage and requires authorization before executing the function body. + +## Usage Example + +Here’s a simple example of using the Ownable module: + +```rust +use soroban_sdk::contract, contractimpl, Address, Env; +use stellar_access::ownable::self as ownable, Ownable; +use stellar_macros::only_owner; + +#[contract] +pub struct MyContract; + +#[contractimpl] +impl MyContract + pub fn __constructor(e: &Env, initial_owner: Address) + // Set the contract owner + ownable::set_owner(e, &initial_owner); + + + #[only_owner] + pub fn update_config(e: &Env, new_value: u32) + // Only the owner can call this function + // Implementation... + + + // This function is accessible to anyone + pub fn get_config(e: &Env) -> u32 + // Implementation... + 42 + + +``` + +## Benefits and Trade-offs + +### Benefits + +* Simple and straightforward ownership model +* Secure two-step ownership transfer process +* Option to permanently renounce ownership +* Easy integration with procedural macro +* Event emission for important actions + +### Trade-offs + +* Limited to a single privileged account (compared to role-based systems) +* Once ownership is renounced, privileged functions become permanently inaccessible + +## See Also + +* [Access Control](/stellar-contracts/access/access-control) +* [Fungible Token](/stellar-contracts/tokens/fungible/fungible) +* [Non-Fungible Token](/stellar-contracts/tokens/non-fungible/non-fungible) diff --git a/content/stellar-contracts/changelog.mdx b/content/stellar-contracts/changelog.mdx index a7021b85..4dc87544 100644 --- a/content/stellar-contracts/changelog.mdx +++ b/content/stellar-contracts/changelog.mdx @@ -95,8 +95,8 @@ In this release, you can find: - Metadata - `Pausable` utility for your contracts. - Examples folder to showcase what's possible: - - fungible-pausable - - fungible-token-interface + - fungible-pausable + - fungible-token-interface - pausable [Changes][v0.1.0] diff --git a/content/stellar-contracts/helpers/default-impl-macro.mdx b/content/stellar-contracts/helpers/default-impl-macro.mdx index b89659cb..1b1ea059 100644 --- a/content/stellar-contracts/helpers/default-impl-macro.mdx +++ b/content/stellar-contracts/helpers/default-impl-macro.mdx @@ -48,12 +48,12 @@ To use the `[default_impl]` macro, place it above the `[contractimp ```rust #[default_impl] // IMPORTANT: place this above `#[contractimpl]` #[contractimpl] -impl NonFungibleToken for MyContract { +impl NonFungibleToken for MyContract type ContractType = Base; // Only override the methods you need to customize // All other methods will be automatically implemented with their default behavior -} + ``` ## How It Works @@ -73,7 +73,7 @@ This process ensures that all trait methods are available to the client generate ### Fungible Token Example ```rust -use soroban_sdk::{contract, contractimpl, Address, Env}; +use soroban_sdk::contract, contractimpl, Address, Env; use stellar_tokens::fungible::FungibleToken; use stellar_macros::default_impl; @@ -82,14 +82,14 @@ pub struct MyToken; #[default_impl] #[contractimpl] -impl FungibleToken for MyToken { +impl FungibleToken for MyToken type ContractType = Base; // Only override methods that need custom behavior - fn transfer(e: &Env, from: Address, to: Address, amount: i128) { + fn transfer(e: &Env, from: Address, to: Address, amount: i128) // custom transfer logic here - } + // All other FungibleToken methods will be automatically implemented -} + ``` diff --git a/content/stellar-contracts/index.mdx b/content/stellar-contracts/index.mdx index de3cf465..71d1ed93 100644 --- a/content/stellar-contracts/index.mdx +++ b/content/stellar-contracts/index.mdx @@ -8,15 +8,15 @@ supporting Fungible, Non-Fungible, and Multi-Token standards. ## Tokens Explore our implementations for token standards on Stellar Soroban: -* ***[Fungible Tokens](tokens/fungible/fungible.adoc)***: Digital assets representing a fixed or dynamic supply of identical units. -* ***[Non-Fungible Tokens](tokens/non-fungible/non-fungible.adoc)***: Unique digital assets with verifiable ownership. +* ***[Fungible Tokens](/stellar-contracts/tokens/fungible/fungible)***: Digital assets representing a fixed or dynamic supply of identical units. +* ***[Non-Fungible Tokens](/stellar-contracts/tokens/non-fungible/non-fungible)***: Unique digital assets with verifiable ownership. * ***Multi-Token***: Hybrid tokens enabling both fungible and non-fungible token functionalities (work in progress). ## Utilities Discover our utility contracts for Stellar Soroban, applicable to all token standards mentioned above: -* ***[Pausable](utils/pausable.adoc)*** -* ***[Upgrades and Migrations](utils/upgradeable.adoc)*** +* ***[Pausable](/stellar-contracts/utils/pausable)*** +* ***[Upgrades and Migrations](/stellar-contracts/utils/upgradeable)*** ## Error Codes In Stellar Soroban, each error variant is assigned an integer. To prevent duplication of error codes, @@ -48,4 +48,4 @@ It is the responsibility of the developer to manage the TTL for instance storage You can find our audit reports [here](https://github.com/OpenZeppelin/stellar-contracts/tree/main/audits). ## Get Started -Get started [here](get-started.adoc). +Get started [here](/stellar-contracts/get-started). diff --git a/content/stellar-contracts/ownable.mdx b/content/stellar-contracts/ownable.mdx index 8de0ce36..d96f0332 100644 --- a/content/stellar-contracts/ownable.mdx +++ b/content/stellar-contracts/ownable.mdx @@ -94,6 +94,6 @@ impl MyContract { ## See Also -* [Access Control](access/access-control.adoc) -* [Fungible Token](tokens/fungible/fungible.adoc) -* [Non-Fungible Token](tokens/non-fungible/non-fungible.adoc) +* [Access Control](/stellar-contracts/access/access-control) +* [Fungible Token](/stellar-contracts/tokens/fungible/fungible) +* [Non-Fungible Token](/stellar-contracts/tokens/non-fungible/non-fungible) diff --git a/content/stellar-contracts/tokens/fungible/fungible.mdx b/content/stellar-contracts/tokens/fungible/fungible.mdx index 991bdca1..51ab8708 100644 --- a/content/stellar-contracts/tokens/fungible/fungible.mdx +++ b/content/stellar-contracts/tokens/fungible/fungible.mdx @@ -31,17 +31,17 @@ and players can transfer tokens between accounts. Here’s what a basic fungible token contract might look like: ```rust -use soroban_sdk::{contract, contractimpl, Address, Env, String}; -use stellar_tokens::fungible::{burnable::FungibleBurnable, Base, ContractOverrides, FungibleToken}; -use stellar_access::ownable::{self as ownable, Ownable}; -use stellar_macros::{default_impl, only_owner}; +use soroban_sdk::contract, contractimpl, Address, Env, String; +use stellar_tokens::fungible::burnable::FungibleBurnable, Base, ContractOverrides, FungibleToken; +use stellar_access::ownable::self as ownable, Ownable; +use stellar_macros::default_impl, only_owner; #[contract] pub struct GameCurrency; #[contractimpl] -impl GameCurrency { - pub fn __constructor(e: &Env, initial_owner: Address) { +impl GameCurrency + pub fn __constructor(e: &Env, initial_owner: Address) // Set token metadata Base::set_metadata( e, @@ -52,24 +52,24 @@ impl GameCurrency { // Set the contract owner ownable::set_owner(e, &initial_owner); - } + #[only_owner] - pub fn mint_tokens(e: &Env, to: Address, amount: i128) { + pub fn mint_tokens(e: &Env, to: Address, amount: i128) // Mint tokens to the recipient Base::mint(e, &to, amount); - } -} + + #[default_impl] #[contractimpl] -impl FungibleToken for GameCurrency { +impl FungibleToken for GameCurrency type ContractType = Base; -} + #[default_impl] #[contractimpl] -impl FungibleBurnable for GameCurrency {} +impl FungibleBurnable for GameCurrency ``` ## Extensions @@ -118,14 +118,14 @@ The package includes utility modules to help with common token implementation pa Provides generic admin functionality similar to the Stellar Asset Contract (SAC). This approach leverages the `__check_auth` function to handle authentication and authorization logic while maintaining a unified interface. -For detailed documentation, see [SAC Admin Generic](tokens/fungible/sac-admin-generic.adoc). +For detailed documentation, see [SAC Admin Generic](/stellar-contracts/tokens/fungible/sac-admin-generic). ### - SAC Admin Wrapper [Source Code](https://github.com/OpenZeppelin/stellar-contracts/tree/main/packages/tokens/src/fungible/utils/sac_admin_wrapper) Provides a wrapper around the SAC admin functionality for easier integration. This approach defines specific entry points for each admin function and forwards calls to the corresponding SAC functions. -For detailed documentation, see [SAC Admin Wrapper](tokens/fungible/sac-admin-wrapper.adoc). +For detailed documentation, see [SAC Admin Wrapper](/stellar-contracts/tokens/fungible/sac-admin-wrapper). ## Compatibility and Compliance diff --git a/content/stellar-contracts/tokens/fungible/sac-admin-generic.mdx b/content/stellar-contracts/tokens/fungible/sac-admin-generic.mdx index bc35c147..b7ac5e4d 100644 --- a/content/stellar-contracts/tokens/fungible/sac-admin-generic.mdx +++ b/content/stellar-contracts/tokens/fungible/sac-admin-generic.mdx @@ -39,44 +39,44 @@ Here’s a simplified example of a SAC Admin Generic contract: #[contracterror] #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] #[repr(u32)] -pub enum SACAdminGenericError { +pub enum SACAdminGenericError Unauthorized = 1, InvalidContext = 2, MintingLimitExceeded = 3, -} + #[contracttype] #[derive(Clone)] -pub struct Signature { +pub struct Signature pub public_key: BytesN<32>, pub signature: BytesN<64>, -} + #[contracttype] -pub enum SacDataKey { +pub enum SacDataKey Chief, Operator(BytesN<32>), // -> true/false MintingLimit(BytesN<32>), // -> (max_limit, curr) -} + #[contract] pub struct SacAdminExampleContract; #[contractimpl] -impl SacAdminExampleContract { - pub fn __constructor(e: Env, sac: Address, chief: BytesN<32>, operator: BytesN<32>) { +impl SacAdminExampleContract + pub fn __constructor(e: Env, sac: Address, chief: BytesN<32>, operator: BytesN<32>) set_sac_address(&e, &sac); e.storage().instance().set(&SacDataKey::Chief, &chief); e.storage().instance().set(&SacDataKey::Operator(operator.clone()), &true); e.storage() .instance() .set(&SacDataKey::MintingLimit(operator), &(1_000_000_000i128, 0i128)); - } + - pub fn get_sac_address(e: &Env) -> Address { + pub fn get_sac_address(e: &Env) -> Address get_sac_address(e) - } -} + + ``` ### Custom Authorization Logic @@ -85,15 +85,15 @@ The key feature of the Generic approach is the ability to implement custom autho function: ```rust -use soroban_sdk::{ - auth::{Context, CustomAccountInterface}, +use soroban_sdk:: + auth::Context, CustomAccountInterface, contract, contracterror, contractimpl, contracttype, crypto::Hash, Address, BytesN, Env, IntoVal, Val, Vec, -}; +; #[contractimpl] -impl CustomAccountInterface for SacAdminExampleContract { +impl CustomAccountInterface for SacAdminExampleContract type Error = SACAdminGenericError; type Signature = Signature; @@ -102,7 +102,7 @@ impl CustomAccountInterface for SacAdminExampleContract { payload: Hash<32>, signature: Self::Signature, auth_context: Vec, - ) -> Result<(), SACAdminGenericError> { + ) -> Result<(), SACAdminGenericError> // authenticate e.crypto().ed25519_verify( &signature.public_key, @@ -112,64 +112,64 @@ impl CustomAccountInterface for SacAdminExampleContract { let caller = signature.public_key.clone(); // extract from context and check required permissions for every function - for ctx in auth_context.iter() { - let context = match ctx { + for ctx in auth_context.iter() + let context = match ctx Context::Contract(c) => c, _ => return Err(SACAdminGenericError::InvalidContext), - }; + ; - match extract_sac_contract_context(&e, &context) { - SacFn::Mint(amount) => { + match extract_sac_contract_context(&e, &context) + SacFn::Mint(amount) => // ensure caller has required permissions ensure_caller_operator(&e, &SacDataKey::Operator(caller.clone()))?; // ensure operator has minting limit ensure_minting_limit(&e, &caller, amount)?; - } - SacFn::Clawback(_amount) => { + + SacFn::Clawback(_amount) => // ensure caller has required permissions ensure_caller_operator(&e, &SacDataKey::Operator(caller.clone()))?; - } - SacFn::SetAuthorized(_) => { + + SacFn::SetAuthorized(_) => // ensure caller has required permissions ensure_caller_operator(&e, &SacDataKey::Operator(caller.clone()))?; - } - SacFn::SetAdmin => { + + SacFn::SetAdmin => // ensure caller has required permissions ensure_caller_chief(&e, &caller, &SacDataKey::Chief)?; - } - SacFn::Unknown => { + + SacFn::Unknown => // ensure only chief can call other functions ensure_caller_chief(&e, &caller, &SacDataKey::Chief)? - } - } - } + + + Ok(()) - } -} + + // Helper functions fn ensure_caller_chief>( e: &Env, caller: &BytesN<32>, key: &K, -) -> Result<(), SACAdminGenericError> { +) -> Result<(), SACAdminGenericError> let operator: BytesN<32> = e.storage().instance().get(key).expect("chief or operator not set"); - if *caller != operator { + if *caller != operator return Err(SACAdminGenericError::Unauthorized); - } + Ok(()) -} + fn ensure_caller_operator>( e: &Env, key: &K, -) -> Result<(), SACAdminGenericError> { - match e.storage().instance().get::<_, bool>(key) { +) -> Result<(), SACAdminGenericError> + match e.storage().instance().get::<_, bool>(key) Some(is_op) if is_op => Ok(()), _ => Err(SACAdminGenericError::Unauthorized), - } -} + + ``` ## Benefits and Trade-offs @@ -193,5 +193,5 @@ A complete example implementation can be found in the ## See Also -* [SAC Admin Wrapper](tokens/fungible/sac-admin-wrapper.adoc) -* [Fungible Token](tokens/fungible/fungible.adoc) +* [SAC Admin Wrapper](/stellar-contracts/tokens/fungible/sac-admin-wrapper) +* [Fungible Token](/stellar-contracts/tokens/fungible/fungible) diff --git a/content/stellar-contracts/tokens/fungible/sac-admin-wrapper.mdx b/content/stellar-contracts/tokens/fungible/sac-admin-wrapper.mdx index 6bf593dc..63eecd4f 100644 --- a/content/stellar-contracts/tokens/fungible/sac-admin-wrapper.mdx +++ b/content/stellar-contracts/tokens/fungible/sac-admin-wrapper.mdx @@ -29,12 +29,12 @@ The Wrapper approach to SAC Admin implementation: The `SACAdminWrapper` trait defines the interface for the wrapper approach: ```rust -pub trait SACAdminWrapper { +pub trait SACAdminWrapper fn set_admin(e: Env, new_admin: Address, operator: Address); fn set_authorized(e: Env, id: Address, authorize: bool, operator: Address); fn mint(e: Env, to: Address, amount: i128, operator: Address); fn clawback(e: Env, from: Address, amount: i128, operator: Address); -} + ``` ### Example Implementation @@ -46,14 +46,14 @@ Here’s a simplified example of a SAC Admin Wrapper contract using the OpenZepp pub struct ExampleContract; #[contractimpl] -impl ExampleContract { +impl ExampleContract pub fn __constructor( e: &Env, default_admin: Address, manager1: Address, manager2: Address, sac: Address, - ) { + ) access_control::set_admin(e, &default_admin); // create a role "manager" and grant it to `manager1` @@ -63,31 +63,31 @@ impl ExampleContract { access_control::grant_role_no_auth(e, &default_admin, &manager2, &symbol_short!("manager")); fungible::sac_admin_wrapper::set_sac_address(e, &sac); - } -} + + #[contractimpl] -impl SACAdminWrapper for ExampleContract { +impl SACAdminWrapper for ExampleContract #[only_admin] - fn set_admin(e: Env, new_admin: Address, _operator: Address) { + fn set_admin(e: Env, new_admin: Address, _operator: Address) fungible::sac_admin_wrapper::set_admin(&e, &new_admin); - } + #[only_role(operator, "manager")] - fn set_authorized(e: Env, id: Address, authorize: bool, operator: Address) { + fn set_authorized(e: Env, id: Address, authorize: bool, operator: Address) fungible::sac_admin_wrapper::set_authorized(&e, &id, authorize); - } + #[only_role(operator, "manager")] - fn mint(e: Env, to: Address, amount: i128, operator: Address) { + fn mint(e: Env, to: Address, amount: i128, operator: Address) fungible::sac_admin_wrapper::mint(&e, &to, amount); - } + #[only_role(operator, "manager")] - fn clawback(e: Env, from: Address, amount: i128, operator: Address) { + fn clawback(e: Env, from: Address, amount: i128, operator: Address) fungible::sac_admin_wrapper::clawback(&e, &from, amount); - } -} + + ``` ### Integration with Access Control @@ -118,5 +118,5 @@ A complete example implementation can be found in the [sac-admin-wrapper example ## See Also -* [SAC Admin Generic](tokens/fungible/sac-admin-generic.adoc) -* [Fungible Token](tokens/fungible/fungible.adoc) +* [SAC Admin Generic](/stellar-contracts/tokens/fungible/sac-admin-generic) +* [Fungible Token](/stellar-contracts/tokens/fungible/fungible) diff --git a/content/stellar-contracts/tokens/non-fungible/nft-consecutive.mdx b/content/stellar-contracts/tokens/non-fungible/nft-consecutive.mdx index f977f4e3..653ce27c 100644 --- a/content/stellar-contracts/tokens/non-fungible/nft-consecutive.mdx +++ b/content/stellar-contracts/tokens/non-fungible/nft-consecutive.mdx @@ -4,55 +4,55 @@ title: Non-Fungible Consecutive [Source Code](https://github.com/OpenZeppelin/stellar-contracts/tree/main/packages/tokens/src/non-fungible/extensions/consecutive) -Consecutive extension for [Non-Fungible Token](tokens/non-fungible/non-fungible.adoc) is useful +Consecutive extension for [Non-Fungible Token](/stellar-contracts/tokens/non-fungible/non-fungible) is useful for efficiently minting multiple tokens in a single transaction. This can significantly reduce costs and improve performance when creating a large number of tokens at once. ## Usage -We’ll continue with the [example](tokens/non-fungible/non-fungible.adoc#usage) from **Non-Fungible Token** +We’ll continue with the [example](/stellar-contracts/tokens/non-fungible/non-fungible#usage) from **Non-Fungible Token** and modify the contract so that now batches of tokens can be minted with each call to `award_items`. Please note any account can call `award_items` and we might want to implement access control to restrict who can mint. ```rust -use soroban_sdk::{contract, contractimpl, Address, Env, String}; +use soroban_sdk::contract, contractimpl, Address, Env, String; use stellar_macros::default_impl; -use stellar_tokens::non_fungible::{ - consecutive::{Consecutive, NonFungibleConsecutive}, +use stellar_tokens::non_fungible:: + consecutive::Consecutive, NonFungibleConsecutive, Base, ContractOverrides, NonFungibleToken, -}; +; #[contract] pub struct GameItem; #[contractimpl] -impl GameItem { - pub fn __constructor(e: &Env) { +impl GameItem + pub fn __constructor(e: &Env) Base::set_metadata( e, String::from_str(e, "www.mygame.com"), String::from_str(e, "My Game Items Collection"), String::from_str(e, "MGMC"), ); - } + - pub fn award_items(e: &Env, to: Address, amount: u32) -> u32 { + pub fn award_items(e: &Env, to: Address, amount: u32) -> u32 // access control might be needed Consecutive::batch_mint(e, &to, amount) - } + - pub fn burn(e: &Env, from: Address, token_id: u32) { + pub fn burn(e: &Env, from: Address, token_id: u32) Consecutive::burn(e, &from, token_id); - } -} + + #[default_impl] #[contractimpl] -impl NonFungibleToken for GameItem { +impl NonFungibleToken for GameItem type ContractType = Consecutive; -} + // no entry-point functions required, marker impl -impl NonFungibleConsecutive for GameItem {} +impl NonFungibleConsecutive for GameItem ``` diff --git a/content/stellar-contracts/tokens/non-fungible/nft-enumerable.mdx b/content/stellar-contracts/tokens/non-fungible/nft-enumerable.mdx index eb4fd57f..f553602f 100644 --- a/content/stellar-contracts/tokens/non-fungible/nft-enumerable.mdx +++ b/content/stellar-contracts/tokens/non-fungible/nft-enumerable.mdx @@ -4,57 +4,57 @@ title: Non-Fungible Enumerable [Source Code](https://github.com/OpenZeppelin/stellar-contracts/tree/main/packages/tokens/src/non-fungible/extensions/enumerable) -Enumerable extension for [Non-Fungible Token](tokens/non-fungible/non-fungible.adoc) allows for enumeration +Enumerable extension for [Non-Fungible Token](/stellar-contracts/tokens/non-fungible/non-fungible) allows for enumeration of all the token IDs in the contract as well as all the token IDs owned by each account. This is useful for applications that need to list or iterate over tokens, such as marketplaces or wallets. ## Usage -We’ll build on the [example](tokens/non-fungible/non-fungible.adoc#usage) from **Non-Fungible Token** +We’ll build on the [example](/stellar-contracts/tokens/non-fungible/non-fungible#usage) from **Non-Fungible Token** and modify the contract so that all tokens an address own can be listed. Please note any account can call `award_item` and we might want to implement access control to restrict who can mint. ```rust -use soroban_sdk::{contract, contractimpl, Address, Env, String}; +use soroban_sdk::contract, contractimpl, Address, Env, String; use stellar_macros::default_impl; -use stellar_tokens::non_fungible::{ - enumerable::{Enumerable, NonFungibleEnumerable}, +use stellar_tokens::non_fungible:: + enumerable::Enumerable, NonFungibleEnumerable, Base, ContractOverrides, NonFungibleToken, -}; +; #[contract] pub struct GameItem; #[contractimpl] -impl GameItem { - pub fn __constructor(e: &Env) { +impl GameItem + pub fn __constructor(e: &Env) Base::set_metadata( e, String::from_str(e, "www.mygame.com"), String::from_str(e, "My Game Items Collection"), String::from_str(e, "MGMC"), ); - } + - pub fn award_item(e: &Env, to: Address) -> u32 { + pub fn award_item(e: &Env, to: Address) -> u32 // access control might be needed Enumerable::sequential_mint(e, &to) - } + - pub fn burn(e: &Env, from: Address, token_id: u32) { + pub fn burn(e: &Env, from: Address, token_id: u32) Enumerable::sequential_burn(e, &from, token_id); - } -} + + #[default_impl] #[contractimpl] -impl NonFungibleToken for GameItem { +impl NonFungibleToken for GameItem type ContractType = Enumerable; -} + #[default_impl] #[contractimpl] -impl NonFungibleEnumerable for GameItem {} +impl NonFungibleEnumerable for GameItem ``` The extension exposes additionally the following entry-point functions, automatically implemented by `#[default_impl]`: diff --git a/content/stellar-contracts/tokens/non-fungible/non-fungible.mdx b/content/stellar-contracts/tokens/non-fungible/non-fungible.mdx index ba43c1df..ff95f123 100644 --- a/content/stellar-contracts/tokens/non-fungible/non-fungible.mdx +++ b/content/stellar-contracts/tokens/non-fungible/non-fungible.mdx @@ -35,42 +35,42 @@ want to implement access control to restrict who can mint. Here’s what a contract for tokenized items might look like: ```rust -use soroban_sdk::{contract, contractimpl, Address, Env, String}; +use soroban_sdk::contract, contractimpl, Address, Env, String; use stellar_macros::default_impl; -use stellar_tokens::non_fungible::{ +use stellar_tokens::non_fungible:: burnable::NonFungibleBurnable, Base, ContractOverrides, NonFungibleToken, -}; +; #[contract] pub struct GameItem; #[contractimpl] -impl GameItem { - pub fn __constructor(e: &Env) { +impl GameItem + pub fn __constructor(e: &Env) Base::set_metadata( e, String::from_str(e, "www.mygame.com"), String::from_str(e, "My Game Items Collection"), String::from_str(e, "MGMC"), ); - } + - pub fn award_item(e: &Env, to: Address) -> u32 { + pub fn award_item(e: &Env, to: Address) -> u32 // access control might be needed Base::sequential_mint(e, &to) - } -} + + #[default_impl] #[contractimpl] -impl NonFungibleToken for GameItem { +impl NonFungibleToken for GameItem type ContractType = Base; -} + #[default_impl] #[contractimpl] -impl NonFungibleBurnable for GameItem {} +impl NonFungibleBurnable for GameItem ``` ## Extensions @@ -85,20 +85,20 @@ The `NonFungibleBurnable` trait extends the `NonFungibleToken` trait to provide ### - Consecutive [Source Code](https://github.com/OpenZeppelin/stellar-contracts/tree/main/packages/tokens/src/non-fungible/extensions/consecutive) -The `NonFungibleConsecutive` extension is optimized for batch minting of tokens with consecutive IDs. This approach drastically reduces storage writes during minting by storing ownership only at boundaries and inferring ownership for other tokens. See [Non-Fungible Consecutive](tokens/non-fungible/nft-consecutive.adoc) for detailed documentation. +The `NonFungibleConsecutive` extension is optimized for batch minting of tokens with consecutive IDs. This approach drastically reduces storage writes during minting by storing ownership only at boundaries and inferring ownership for other tokens. See [Non-Fungible Consecutive](/stellar-contracts/tokens/non-fungible/nft-consecutive) for detailed documentation. This extension is build around the contract variant `Consecutive`. Here is an example usage: -* [Non-Fungible Consecutive](tokens/non-fungible/nft-consecutive.adoc) +* [Non-Fungible Consecutive](/stellar-contracts/tokens/non-fungible/nft-consecutive) ### - Enumerable [Source Code](https://github.com/OpenZeppelin/stellar-contracts/tree/main/packages/tokens/src/non-fungible/extensions/enumerable) -The `NonFungibleEnumerable` extension enables on-chain enumeration of tokens owned by an address. See [Non-Fungible Enumerable](tokens/non-fungible/nft-enumerable.adoc) for detailed documentation. +The `NonFungibleEnumerable` extension enables on-chain enumeration of tokens owned by an address. See [Non-Fungible Enumerable](/stellar-contracts/tokens/non-fungible/nft-enumerable) for detailed documentation. This extension is build around the contract variant `Enumerable`. Here is an example usage: -* [Non-Fungible Enumerable](tokens/non-fungible/nft-enumerable.adoc) +* [Non-Fungible Enumerable](/stellar-contracts/tokens/non-fungible/nft-enumerable) ### - Royalties [Source Code](https://github.com/OpenZeppelin/stellar-contracts/tree/main/packages/tokens/src/non-fungible/extensions/royalties) diff --git a/content/stellar-contracts/utils/crypto.mdx b/content/stellar-contracts/utils/crypto.mdx index b50af954..1b2bd66d 100644 --- a/content/stellar-contracts/utils/crypto.mdx +++ b/content/stellar-contracts/utils/crypto.mdx @@ -32,13 +32,13 @@ Provides a generic `Hasher` trait and implementations for common hash functions: Each hasher follows the same interface: ```rust -pub trait Hasher { +pub trait Hasher type Output; fn new(e: &Env) -> Self; fn update(&mut self, input: Bytes); fn finalize(self) -> Self::Output; -} + ``` #### Hashable @@ -46,9 +46,9 @@ pub trait Hasher { The `Hashable` trait allows types to be hashed with any `Hasher` implementation: ```rust -pub trait Hashable { +pub trait Hashable fn hash(&self, hasher: &mut H); -} + ``` Built-in implementations are provided for `BytesN<32>` and `Bytes`. @@ -66,11 +66,11 @@ The `Verifier` struct provides functionality to verify Merkle proofs: impl Verifier where H: Hasher, -{ - pub fn verify(e: &Env, proof: Vec, root: Bytes32, leaf: Bytes32) -> bool { + + pub fn verify(e: &Env, proof: Vec, root: Bytes32, leaf: Bytes32) -> bool // Implementation verifies that the leaf is part of the tree defined by root - } -} + + ``` ### Usage Examples @@ -78,7 +78,7 @@ where #### Hashing Data ```rust -use soroban_sdk::{Bytes, Env}; +use soroban_sdk::Bytes, Env; use stellar_contract_utils::crypto::keccak::Keccak256; use stellar_contract_utils::crypto::hasher::Hasher; @@ -94,7 +94,7 @@ let hash = hasher.finalize(); #### Verifying a Merkle Proof ```rust -use soroban_sdk::{BytesN, Env, Vec}; +use soroban_sdk::BytesN, Env, Vec; use stellar_crypto::keccak::Keccak256; use stellar_crypto::merkle::Verifier; @@ -119,9 +119,9 @@ other assets using Merkle proofs for verification. The `IndexableLeaf` trait defines the structure for nodes in the Merkle tree: ```rust -pub trait IndexableLeaf { +pub trait IndexableLeaf fn index(&self) -> u32; -} + ``` Each node must include a unique index that identifies its position in the Merkle tree. @@ -137,49 +137,49 @@ The `MerkleDistributor` struct provides functionality for: ### Usage Example ```rust -use soroban_sdk::{contract, contractimpl, contracttype, Address, BytesN, Env, Vec}; +use soroban_sdk::contract, contractimpl, contracttype, Address, BytesN, Env, Vec; use stellar_contract_utils::crypto::keccak::Keccak256; -use stellar_contract_utils::merkle_distributor::{IndexableLeaf, MerkleDistributor}; +use stellar_contract_utils::merkle_distributor::IndexableLeaf, MerkleDistributor; // Define a leaf node structure #[contracttype] -struct LeafData { +struct LeafData pub index: u32, pub address: Address, pub amount: i128, -} + // Implement IndexableLeaf for the leaf structure -impl IndexableLeaf for LeafData { - fn index(&self) -> u32 { +impl IndexableLeaf for LeafData + fn index(&self) -> u32 self.index - } -} + + #[contract] pub struct TokenDistributor; #[contractimpl] -impl TokenDistributor { +impl TokenDistributor // Initialize the distributor with a Merkle root - pub fn initialize(e: &Env, root: BytesN<32>) { + pub fn initialize(e: &Env, root: BytesN<32>) MerkleDistributor::::set_root(e, root); - } + // Claim tokens by providing a proof - pub fn claim(e: &Env, leaf: LeafData, proof: Vec>) { + pub fn claim(e: &Env, leaf: LeafData, proof: Vec>) // Verify the proof and mark as claimed MerkleDistributor::::verify_and_set_claimed(e, leaf.clone(), proof); // Transfer tokens or perform other actions based on leaf data // ... - } + // Check if an index has been claimed - pub fn is_claimed(e: &Env, index: u32) -> bool { + pub fn is_claimed(e: &Env, index: u32) -> bool MerkleDistributor::::is_claimed(e, index) - } -} + + ``` ## Use Cases diff --git a/content/stellar-contracts/utils/pausable.mdx b/content/stellar-contracts/utils/pausable.mdx index a462ba09..3c03df84 100644 --- a/content/stellar-contracts/utils/pausable.mdx +++ b/content/stellar-contracts/utils/pausable.mdx @@ -17,17 +17,17 @@ To make it easier to spot when inspecting the code, we turned this simple functi An example: ```rust #[when_paused] -pub fn emergency_reset(e: &Env) { +pub fn emergency_reset(e: &Env) e.storage().instance().set(&DataKey::Counter, &0); -} + ``` Which will expand into the below code: ```rust -pub fn emergency_reset(e: &Env) { +pub fn emergency_reset(e: &Env) when_paused(e); e.storage().instance().set(&DataKey::Counter, &0); -} + ``` diff --git a/content/stellar-contracts/utils/upgradeable.mdx b/content/stellar-contracts/utils/upgradeable.mdx index 7c6cb452..e8332bdf 100644 --- a/content/stellar-contracts/utils/upgradeable.mdx +++ b/content/stellar-contracts/utils/upgradeable.mdx @@ -23,8 +23,8 @@ provides a lightweight upgradeability framework with additional support for stru It consists of two main components: -1. ***[`Upgradeable`](utils/upgradeable.adoc#upgrade_only)*** for cases where only the WASM binary needs to be updated. -2. ***[`UpgradeableMigratable`](utils/upgradeable.adoc#upgrade_and_migrate)*** for more advanced scenarios where, in addition to the WASM binary, specific storage entries +1. ***[`Upgradeable`](#upgrade_only)*** for cases where only the WASM binary needs to be updated. +2. ***[`UpgradeableMigratable`](#upgrade_and_migrate)*** for more advanced scenarios where, in addition to the WASM binary, specific storage entries must be modified (migrated) during the upgrade process. The recommended way to use this module is through the `\#[derive(Upgradeable)]` and `#[derive(UpgradeableMigratable)]` @@ -35,7 +35,7 @@ and access control. These derive macros also leverage the crate version from the the binary version in the WASM metadata, aligning with the guidelines outlined in [SEP-49](https://github.com/stellar/stellar-protocol/blob/master/ecosystem%2Fsep-0049.md). - + While the framework structures the upgrade flow, it does NOT perform deeper checks and verifications such as: @@ -43,7 +43,7 @@ While the framework structures the upgrade flow, it does NOT perform deeper chec * Verifying that the new contract includes an upgradability mechanism, preventing an unintended loss of further upgradability capacity. * Checking for storage consistency, ensuring that the new contract does not inadvertently introduce storage mismatches. - + ## Usage @@ -56,41 +56,41 @@ specifying who can perform the upgrade. This minimal implementation keeps the fo permissions. ```rust -use soroban_sdk::{ +use soroban_sdk:: contract, contracterror, contractimpl, panic_with_error, symbol_short, Address, Env, -}; +; use stellar_contract_utils::upgradeable::UpgradeableInternal; use stellar_macros::Upgradeable; #[contracterror] #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] #[repr(u32)] -pub enum ExampleContractError { +pub enum ExampleContractError Unauthorized = 1, -} + #[derive(Upgradeable)] #[contract] pub struct ExampleContract; #[contractimpl] -impl ExampleContract { - pub fn __constructor(e: &Env, admin: Address) { +impl ExampleContract + pub fn __constructor(e: &Env, admin: Address) e.storage().instance().set(&symbol_short!("OWNER"), &admin); - } -} + + -impl UpgradeableInternal for ExampleContract { - fn _require_auth(e: &Env, operator: &Address) { +impl UpgradeableInternal for ExampleContract + fn _require_auth(e: &Env, operator: &Address) operator.require_auth(); // `operator` is the invoker of the upgrade function and can be used // to perform a role-based access control if implemented let owner: Address = e.storage().instance().get(&symbol_short!("OWNER")).unwrap(); - if *operator != owner { + if *operator != owner panic_with_error!(e, ExampleContractError::Unauthorized) - } - } -} + + + ``` ### Upgrade and Migrate @@ -104,44 +104,44 @@ The `#[derive(UpgradeableMigratable)]` macro manages the sequencing of operation only be invoked after a successful upgrade, preventing potential state inconsistencies and storage corruption. ```rust -use soroban_sdk::{ +use soroban_sdk:: contract, contracterror, contracttype, panic_with_error, symbol_short, Address, Env, -}; +; use stellar_contract_utils::upgradeable::UpgradeableMigratableInternal; use stellar_macros::UpgradeableMigratable; #[contracterror] #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] #[repr(u32)] -pub enum ExampleContractError { +pub enum ExampleContractError Unauthorized = 1, -} + #[contracttype] -pub struct Data { +pub struct Data pub num1: u32, pub num2: u32, -} + #[derive(UpgradeableMigratable)] #[contract] pub struct ExampleContract; -impl UpgradeableMigratableInternal for ExampleContract { +impl UpgradeableMigratableInternal for ExampleContract type MigrationData = Data; - fn _require_auth(e: &Env, operator: &Address) { + fn _require_auth(e: &Env, operator: &Address) operator.require_auth(); let owner: Address = e.storage().instance().get(&symbol_short!("OWNER")).unwrap(); - if *operator != owner { + if *operator != owner panic_with_error!(e, ExampleContractError::Unauthorized) - } - } + + - fn _migrate(e: &Env, data: &Self::MigrationData) { + fn _migrate(e: &Env, data: &Self::MigrationData) e.storage().instance().set(&symbol_short!("DATA_KEY"), data); - } -} + + ``` @@ -158,21 +158,21 @@ atomic upgrade-and-migrate process. This approach ensures that the migration log upgrade without requiring a separate transaction. ```rust -use soroban_sdk::{contract, contractimpl, symbol_short, Address, BytesN, Env, Val}; +use soroban_sdk::contract, contractimpl, symbol_short, Address, BytesN, Env, Val; use stellar_contract_utils::upgradeable::UpgradeableClient; #[contract] pub struct Upgrader; #[contractimpl] -impl Upgrader { +impl Upgrader pub fn upgrade_and_migrate( env: Env, contract_address: Address, operator: Address, wasm_hash: BytesN<32>, migration_data: soroban_sdk::Vec, - ) { + ) operator.require_auth(); let contract_client = UpgradeableClient::new(&env, &contract_address); @@ -180,6 +180,6 @@ impl Upgrader { // The types of the arguments to the migrate function are unknown to this // contract, so we need to call it with invoke_contract. env.invoke_contract::<()>(&contract_address, &symbol_short!("migrate"), migration_data); - } -} + + ``` diff --git a/src/navigation/stellar.json b/src/navigation/stellar.json index 9c727328..35721844 100644 --- a/src/navigation/stellar.json +++ b/src/navigation/stellar.json @@ -20,12 +20,12 @@ { "type": "page", "name": "Access Control", - "url": "/stellar-contracts/access-control" + "url": "/stellar-contracts/access/access-control" }, { "type": "page", "name": "Ownable", - "url": "/stellar-contracts/ownable" + "url": "/stellar-contracts/access/ownable" } ] }, @@ -34,14 +34,46 @@ "name": "Tokens", "children": [ { - "type": "page", - "name": "Fungible Tokens", - "url": "/stellar-contracts/tokens/fungible/fungible" + "type": "folder", + "name": "Fungible", + "children": [ + { + "type": "page", + "name": "Fungible Tokens", + "url": "/stellar-contracts/tokens/fungible/fungible" + }, + { + "type": "page", + "name": "SAC Admin Generic", + "url": "/stellar-contracts/tokens/fungible/sac-admin-generic" + }, + { + "type": "page", + "name": "SAC Admin Wrapper", + "url": "/stellar-contracts/tokens/fungible/sac-admin-wrapper" + } + ] }, { - "type": "page", - "name": "Non-Fungible Tokens", - "url": "/stellar-contracts/tokens/non-fungible/non-fungible" + "type": "folder", + "name": "Non-Fungible", + "children": [ + { + "type": "page", + "name": "Non-Fungible Tokens", + "url": "/stellar-contracts/tokens/non-fungible/non-fungible" + }, + { + "type": "page", + "name": "NFT Consecutive", + "url": "/stellar-contracts/tokens/non-fungible/nft-consecutive" + }, + { + "type": "page", + "name": "NFT Enumerable", + "url": "/stellar-contracts/tokens/non-fungible/nft-enumerable" + } + ] } ] }, @@ -66,6 +98,17 @@ } ] }, + { + "type": "folder", + "name": "Helpers", + "children": [ + { + "type": "page", + "name": "Default Implementation Macro", + "url": "/stellar-contracts/helpers/default-impl-macro" + } + ] + }, { "type": "page", "name": "Changelog", From 403d8f6ce6007d77fd24954350291cde925d0049 Mon Sep 17 00:00:00 2001 From: Steve Date: Mon, 8 Sep 2025 16:16:26 -0400 Subject: [PATCH 03/14] chore: updated AGENTS.md with approach for fixing cairo api --- AGENTS.md | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 814d57d4..2895004c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -50,3 +50,69 @@ The site uses a **modular JSON navigation system** instead of fumadocs meta.json - PascalCase for React components and interfaces - camelCase for variables and functions - kebab-case for file names in content directories + +## Markdown Link Cleanup Strategy + +### Problem +Legacy markdown files often contain complex, unreadable link syntax that creates URL-encoded URLs like: +``` +http://localhost:3000/api/access#has_role(role:-felt252,-account:-contractaddress)-%E2%86%92-bool-external +``` + +### Solution Approach +When cleaning up markdown files with complex link syntax, follow this systematic approach: + +#### 1. Identify Complex Link Patterns +Look for these problematic patterns: +- **Function list links**: `[`+function_name+`](#`[.contract-item-name]#`function_name`#`(params)``-[.item-kind]#external#)` +- **Event list links**: `[`+EventName+`](#`[.contract-item-name]#`eventname`#`(params)``-[.item-kind]#event#)` +- **Function headings**: `#### `[.contract-item-name]#`function_name`#`(params)`` [.item-kind]#external#` + +#### 2. Create Clean Link Strategy +Replace with simple, readable format: +- **Clean list links**: `[`function_name`](#prefix-function_name)` +- **Clean headings with anchors**: + ```markdown + + #### `function_name(params) → return_type` + ``` + +#### 3. Anchor ID Naming Convention +Use descriptive prefixes to avoid conflicts: +- `iaccesscontrol-` for interface functions/events +- `component-` for component functions/events +- `extension-` for extension functions/events + +#### 4. Implementation Process +1. **Use Task agent** for systematic fixes across large files +2. **Fix by section** - group related functions/events together +3. **Update both links and targets** - ensure table of contents links point to correct anchors +4. **Verify no complex patterns remain** - search for `[.contract-item-name]#` and `[.item-kind]#` + +#### 5. Benefits +- ✅ Clean, readable URLs +- ✅ Better SEO and user experience +- ✅ Easier maintenance and debugging +- ✅ Consistent markdown formatting + +### Example Before/After + +**Before (Complex):** +```markdown +* [`+has_role(role, account)+`](#`[.contract-item-name]#`has_role`#`(role:-felt252,-account:-contractaddress)-→-bool``-[.item-kind]#external#) + +#### `[.contract-item-name]#`has_role`#`(role: felt252, account: ContractAddress) → bool`` [.item-kind]#external# +``` + +**After (Clean):** +```markdown +* [`has_role(role, account)`](#iaccesscontrol-has_role) + + +#### `has_role(role: felt252, account: ContractAddress) → bool` +``` + +### Framework Compatibility Notes +- **DO NOT use `{#anchor}` syntax** - breaks the framework parser +- **USE HTML anchor tags** - `` format is safe +- **Test locally** to ensure links work properly after changes From 4d7ae2d43840bb52a0b7cb7d29249422aa1a6fb0 Mon Sep 17 00:00:00 2001 From: Steve Date: Mon, 8 Sep 2025 16:51:28 -0400 Subject: [PATCH 04/14] chore: added back in API reference for starknet --- content/cairo-contracts/api/access.mdx | 1239 ++++++++ content/cairo-contracts/api/account.mdx | 614 ++++ content/cairo-contracts/api/erc1155.mdx | 553 ++++ content/cairo-contracts/api/erc20.mdx | 1138 +++++++ content/cairo-contracts/api/erc721.mdx | 812 +++++ content/cairo-contracts/api/finance.mdx | 224 ++ content/cairo-contracts/api/governance.mdx | 2769 +++++++++++++++++ content/cairo-contracts/api/introspection.mdx | 72 + content/cairo-contracts/api/merkle-tree.mdx | 143 + content/cairo-contracts/api/security.mdx | 150 + content/cairo-contracts/api/token_common.mdx | 347 +++ content/cairo-contracts/api/udc.mdx | 65 + content/cairo-contracts/api/upgrades.mdx | 102 + content/cairo-contracts/api/utilities.mdx | 283 ++ src/navigation/starknet.json | 76 + 15 files changed, 8587 insertions(+) create mode 100644 content/cairo-contracts/api/access.mdx create mode 100644 content/cairo-contracts/api/account.mdx create mode 100644 content/cairo-contracts/api/erc1155.mdx create mode 100644 content/cairo-contracts/api/erc20.mdx create mode 100644 content/cairo-contracts/api/erc721.mdx create mode 100644 content/cairo-contracts/api/finance.mdx create mode 100644 content/cairo-contracts/api/governance.mdx create mode 100644 content/cairo-contracts/api/introspection.mdx create mode 100644 content/cairo-contracts/api/merkle-tree.mdx create mode 100644 content/cairo-contracts/api/security.mdx create mode 100644 content/cairo-contracts/api/token_common.mdx create mode 100644 content/cairo-contracts/api/udc.mdx create mode 100644 content/cairo-contracts/api/upgrades.mdx create mode 100644 content/cairo-contracts/api/utilities.mdx diff --git a/content/cairo-contracts/api/access.mdx b/content/cairo-contracts/api/access.mdx new file mode 100644 index 00000000..c3716fe4 --- /dev/null +++ b/content/cairo-contracts/api/access.mdx @@ -0,0 +1,1239 @@ +--- +title: Access Control +--- + +This crate provides ways to restrict who can access the functions of a contract or when they can do it. + +- [Ownable](#OwnableComponent) is a simple mechanism with a single "owner" role that can be assigned to a single account. This mechanism can be useful in simple scenarios, but fine grained access needs are likely to outgrow it. +- [AccessControl](#AccessControlComponent) provides a general role based access control mechanism. Multiple hierarchical roles can be created and assigned each to multiple accounts. + +## [](#interfaces)Interfaces + +Starting from version `3.x.x`, the interfaces are no longer part of the `openzeppelin_access` package. The references documented here are contained in the `openzeppelin_interfaces` package version `v2.1.0`. + +### [](#IAccessControl)`IAccessControl`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/interfaces/src/access/accesscontrol.cairo) + +```cairo +use openzeppelin_interfaces::accesscontrol::IAccessControl; +``` + +External interface of AccessControl. + +[SRC5 ID](introspection#ISRC5) + +0x23700be02858dbe2ac4dc9c9f66d0b6b0ed81ec7f970ca6844500a56ff61751 + +Functions + +- [`has_role(role, account)`](#IAccessControl-has_role) +- [`get_role_admin(role)`](#IAccessControl-get_role_admin) +- [`grant_role(role, account)`](#IAccessControl-grant_role) +- [`revoke_role(role, account)`](#IAccessControl-revoke_role) +- [`renounce_role(role, account)`](#IAccessControl-renounce_role) + +Events + +- [`RoleAdminChanged(role, previous_admin_role, new_admin_role)`](#IAccessControl-RoleAdminChanged) +- [`RoleGranted(role, account, sender)`](#IAccessControl-RoleGranted) +- [`RoleRevoked(role, account, sender)`](#IAccessControl-RoleRevoked) + +#### [](#IAccessControl-Functions)Functions + +#### [](#IAccessControl-has_role)`has_role(role: felt252, account: ContractAddress) → bool` external + +Returns whether `account` can act as `role`. + +#### [](#IAccessControl-get_role_admin)`get_role_admin(role: felt252) → felt252` external + +Returns the admin role that controls `role`. See [grant\_role](#IAccessControl-grant_role) and [revoke\_role](#IAccessControl-revoke_role). + +To change a role’s admin, use [set\_role\_admin](#AccessControlComponent-set_role_admin). + +#### [](#IAccessControl-grant_role)`grant_role(role: felt252, account: ContractAddress)` external + +Grants `role` to `account`. + +If `account` had not been already granted `role`, emits a [RoleGranted](#IAccessControl-RoleGranted) event. + +Requirements: + +- the caller must have `role`'s admin role. + +#### [](#IAccessControl-revoke_role)`revoke_role(role: felt252, account: ContractAddress)` external + +Revokes `role` from `account`. + +If `account` had been granted `role`, emits a [RoleRevoked](#IAccessControl-RoleRevoked) event. + +Requirements: + +- the caller must have `role`'s admin role. + +#### [](#IAccessControl-renounce_role)`renounce_role(role: felt252, account: ContractAddress)` external + +Revokes `role` from the calling account. + +Roles are often managed via [grant\_role](#IAccessControl-grant_role) and [revoke\_role](#IAccessControl-revoke_role). This function’s purpose is to provide a mechanism for accounts to lose their privileges if they are compromised (such as when a trusted device is misplaced). + +If the calling account had been granted `role`, emits a [RoleRevoked](#IAccessControl-RoleRevoked) event. + +Requirements: + +- the caller must be `account`. + +#### [](#IAccessControl-Events)Events + +#### [](#IAccessControl-RoleAdminChanged)`RoleAdminChanged(role: felt252, previous_admin_role: ContractAddress, new_admin_role: ContractAddress)` event + +Emitted when `new_admin_role` is set as `role`'s admin role, replacing `previous_admin_role` + +`DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite [RoleAdminChanged](#IAccessControl-RoleAdminChanged) not being emitted signaling this. + +#### [](#IAccessControl-RoleGranted)`RoleGranted(role: felt252, account: ContractAddress, sender: ContractAddress)` event + +Emitted when `account` is granted `role`. + +`sender` is the account that originated the contract call, an account with the admin role or the deployer address if `_grant_role` is called from the constructor. + +#### [](#IAccessControl-RoleRevoked)`RoleRevoked(role: felt252, account: ContractAddress, sender: ContractAddress)` event + +Emitted when `account` is revoked `role`. + +`sender` is the account that originated the contract call: + +- if using `revoke_role`, it is the admin role bearer. +- if using `renounce_role`, it is the role bearer (i.e. `account`). + +### [](#IAccessControlWithDelay)`IAccessControlWithDelay`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/interfaces/src/access/accesscontrol.cairo) + +```cairo +use openzeppelin_interfaces::accesscontrol::IAccessControlWithDelay; +``` + +External interface for the extended `AccessControlWithDelay` functionality. + +Functions + +- [`get_role_status(role, account)`](#IAccessControlWithDelay-get_role_status) +- [`grant_role_with_delay(role, account, delay)`](#IAccessControlWithDelay-grant_role_with_delay) + +Events + +- [`RoleGrantedWithDelay(role, account, sender, delay)`](#IAccessControlWithDelay-RoleGrantedWithDelay) + +#### [](#IAccessControlWithDelay-Functions)Functions + +#### [](#IAccessControlWithDelay-get_role_status)`get_role_status(role: felt252, account: ContractAddress) → RoleStatus` external + +Returns the account’s status for the given role. The possible statuses are: + +- `NotGranted`: the role has not been granted to the account. +- `Delayed`: The role has been granted to the account but is not yet active due to a time delay. +- `Effective`: the role has been granted to the account and is currently active. + +#### [](#IAccessControlWithDelay-grant_role_with_delay)`grant_role_with_delay(role: felt252, account: ContractAddress, delay: u64)` external + +Attempts to grant `role` to `account` with the specified activation delay. + +Requirements: + +- The caller must have `role`'s admin role. +- delay must be greater than 0. +- the `role` must not be already effective for `account`. + +May emit a [RoleGrantedWithDelay](#IAccessControlWithDelay-RoleGrantedWithDelay) event. + +#### [](#IAccessControlWithDelay-Events)Events + +#### [](#IAccessControlWithDelay-RoleGrantedWithDelay)`RoleGrantedWithDelay(role: felt252, account: ContractAddress, sender: ContractAddress, delay: u64)` event + +Emitted when `account` is granted `role` with a delay. + +`sender` is the account that originated the contract call, an account with the admin role or the deployer address if [\_grant\_role\_with\_delay](#AccessControlComponent-_grant_role_with_delay) is called from the constructor. + +### [](#IAccessControlDefaultAdminRules)`IAccessControlDefaultAdminRules`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/interfaces/src/access/accesscontrol_default_admin_rules.cairo) + +```cairo +use openzeppelin_interfaces::accesscontrol_default_admin_rules::IAccessControlDefaultAdminRules; +``` + +External interface of AccessControlDefaultAdminRules declared to support [SRC5](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-5.md) detection. + +[SRC5 ID](introspection#ISRC5) + +0x3509b3083c9586afe5dae781146b0608c3846870510f8d4d21ae38676cc33eb + +Functions + +- [`default_admin()`](#IAccessControlDefaultAdminRules-default_admin) +- [`pending_default_admin()`](#IAccessControlDefaultAdminRules-pending_default_admin) +- [`default_admin_delay()`](#IAccessControlDefaultAdminRules-default_admin_delay) +- [`pending_default_admin_delay()`](#IAccessControlDefaultAdminRules-pending_default_admin_delay) +- [`begin_default_admin_transfer(new_admin)`](#IAccessControlDefaultAdminRules-begin_default_admin_transfer) +- [`cancel_default_admin_transfer()`](#IAccessControlDefaultAdminRules-cancel_default_admin_transfer) +- [`accept_default_admin_transfer()`](#IAccessControlDefaultAdminRules-accept_default_admin_transfer) +- [`change_default_admin_delay(new_delay)`](#IAccessControlDefaultAdminRules-change_default_admin_delay) +- [`rollback_default_admin_delay()`](#IAccessControlDefaultAdminRules-rollback_default_admin_delay) +- [`default_admin_delay_increase_wait()`](#IAccessControlDefaultAdminRules-default_admin_delay_increase_wait) + +Events + +- [`DefaultAdminTransferScheduled(new_admin, accept_schedule)`](#IAccessControlDefaultAdminRules-DefaultAdminTransferScheduled) +- [`DefaultAdminTransferCanceled()`](#IAccessControlDefaultAdminRules-DefaultAdminTransferCanceled) +- [`DefaultAdminDelayChangeScheduled(new_delay, effect_schedule)`](#IAccessControlDefaultAdminRules-DefaultAdminDelayChangeScheduled) +- [`DefaultAdminDelayChangeCanceled()`](#IAccessControlDefaultAdminRules-DefaultAdminDelayChangeCanceled) + +#### [](#IAccessControlDefaultAdminRules-Functions)Functions + +#### [](#IAccessControlDefaultAdminRules-default_admin)`default_admin() → ContractAddress` external + +Returns the address of the current `DEFAULT_ADMIN_ROLE` holder. + +#### [](#IAccessControlDefaultAdminRules-pending_default_admin)`pending_default_admin() → (ContractAddress, u64)` external + +Returns a tuple of a `new_admin` and an `accept_schedule`. + +After the `accept_schedule` passes, the `new_admin` will be able to accept the `default_admin` role by calling [accept\_default\_admin\_transfer](#IAccessControlDefaultAdminRules-accept_default_admin_transfer), completing the role transfer. + +A zero value only in `accept_schedule` indicates no pending admin transfer. + +A zero address `new_admin` means that `default_admin` is being renounced. + +#### [](#IAccessControlDefaultAdminRules-default_admin_delay)`default_admin_delay() → u64` external + +Returns the delay required to schedule the acceptance of a `default_admin` transfer started. + +This delay will be added to the current timestamp when calling [begin\_default\_admin\_transfer](#IAccessControlDefaultAdminRules-begin_default_admin_transfer) to set the acceptance schedule. + +If a delay change has been scheduled, it will take effect as soon as the schedule passes, making this function return the new delay. + +See [change\_default\_admin\_delay](#IAccessControlDefaultAdminRules-change_default_admin_delay). + +#### [](#IAccessControlDefaultAdminRules-pending_default_admin_delay)`pending_default_admin_delay() → (u64, u64)` external + +Returns a tuple of `new_delay` and an `effect_schedule`. + +After the `effect_schedule` passes, the `new_delay` will get into effect immediately for every new `default_admin` transfer started with [begin\_default\_admin\_transfer](#IAccessControlDefaultAdminRules-begin_default_admin_transfer). + +A zero value only in `effect_schedule` indicates no pending delay change. + +A zero value only for `new_delay` means that the next [default\_admin\_delay](#IAccessControlDefaultAdminRules-default_admin_delay) will be zero after the effect schedule. + +#### [](#IAccessControlDefaultAdminRules-begin_default_admin_transfer)`begin_default_admin_transfer(new_admin)` external + +Starts a `default_admin` transfer by setting a [pending\_default\_admin](#IAccessControlDefaultAdminRules-pending_default_admin) scheduled for acceptance after the current timestamp plus a [default\_admin\_delay](#IAccessControlDefaultAdminRules-default_admin_delay). + +Requirements: + +- Only can be called by the current `default_admin`. + +Emits a [DefaultAdminTransferScheduled](#IAccessControlDefaultAdminRules-DefaultAdminTransferScheduled) event. + +#### [](#IAccessControlDefaultAdminRules-cancel_default_admin_transfer)`cancel_default_admin_transfer()` external + +Cancels a `default_admin` transfer previously started with [begin\_default\_admin\_transfer](#IAccessControlDefaultAdminRules-begin_default_admin_transfer). + +A [pending\_default\_admin](#IAccessControlDefaultAdminRules-pending_default_admin) not yet accepted can also be cancelled with this function. + +Requirements: + +- Only can be called by the current `default_admin`. + +May emit a [DefaultAdminTransferCanceled](#IAccessControlDefaultAdminRules-DefaultAdminTransferCanceled) event. + +#### [](#IAccessControlDefaultAdminRules-accept_default_admin_transfer)`accept_default_admin_transfer()` external + +Completes a `default_admin` transfer previously started with [begin\_default\_admin\_transfer](#IAccessControlDefaultAdminRules-begin_default_admin_transfer). + +After calling the function: + +- `DEFAULT_ADMIN_ROLE` must be granted to the caller. +- `DEFAULT_ADMIN_ROLE` must be revoked from the previous holder. +- [pending\_default\_admin](#IAccessControlDefaultAdminRules-pending_default_admin) must be reset to zero value. + +Requirements: + +- Only can be called by the [pending\_default\_admin](#IAccessControlDefaultAdminRules-pending_default_admin)'s `new_admin`. +- The [pending\_default\_admin](#IAccessControlDefaultAdminRules-pending_default_admin)'s `accept_schedule` should’ve passed. + +#### [](#IAccessControlDefaultAdminRules-change_default_admin_delay)`change_default_admin_delay(new_delay)` external + +Initiates a [default\_admin\_delay](#IAccessControlDefaultAdminRules-default_admin_delay) update by setting a [pending\_default\_admin\_delay](#IAccessControlDefaultAdminRules-pending_default_admin_delay) scheduled to take effect after the current timestamp plus a [default\_admin\_delay](#IAccessControlDefaultAdminRules-default_admin_delay). + +This function guarantees that any call to [begin\_default\_admin\_transfer](#IAccessControlDefaultAdminRules-begin_default_admin_transfer) done between the timestamp this method is called and the [pending\_default\_admin\_delay](#IAccessControlDefaultAdminRules-pending_default_admin_delay) effect schedule will use the current [default\_admin\_delay](#IAccessControlDefaultAdminRules-default_admin_delay) set before calling. + +The [pending\_default\_admin\_delay](#IAccessControlDefaultAdminRules-pending_default_admin_delay)'s effect schedule is defined in a way that waiting until the schedule and then calling [begin\_default\_admin\_transfer](#IAccessControlDefaultAdminRules-begin_default_admin_transfer) with the new delay will take at least the same as another `default_admin` complete transfer (including acceptance). + +The schedule is designed for two scenarios: + +- When the delay is changed for a larger one the schedule is `block.timestamp + new delay` capped by [default\_admin\_delay\_increase\_wait](#IAccessControlDefaultAdminRules-default_admin_delay_increase_wait). +- When the delay is changed for a shorter one, the schedule is `block.timestamp + (current delay - new delay)`. + +A [pending\_default\_admin\_delay](#IAccessControlDefaultAdminRules-pending_default_admin_delay) that never got into effect will be canceled in favor of a new scheduled change. + +Requirements: + +- Only can be called by the current `default_admin`. + +Emits a [DefaultAdminDelayChangeScheduled](#IAccessControlDefaultAdminRules-DefaultAdminDelayChangeScheduled) event and may emit a [DefaultAdminDelayChangeCanceled](#IAccessControlDefaultAdminRules-DefaultAdminDelayChangeCanceled) event. + +#### [](#IAccessControlDefaultAdminRules-rollback_default_admin_delay)`rollback_default_admin_delay()` external + +Cancels a scheduled [default\_admin\_delay](#IAccessControlDefaultAdminRules-default_admin_delay) change. + +Requirements: + +- Only can be called by the current `default_admin`. + +May emit a [DefaultAdminDelayChangeCanceled](#IAccessControlDefaultAdminRules-DefaultAdminDelayChangeCanceled) event. + +#### [](#IAccessControlDefaultAdminRules-default_admin_delay_increase_wait)`default_admin_delay_increase_wait() → u64` external + +Maximum time in seconds for an increase to [default\_admin\_delay](#IAccessControlDefaultAdminRules-default_admin_delay) (that is scheduled using [change\_default\_admin\_delay](#IAccessControlDefaultAdminRules-change_default_admin_delay)) to take effect. Defaults to 5 days. + +When the [default\_admin\_delay](#IAccessControlDefaultAdminRules-default_admin_delay) is scheduled to be increased, it goes into effect after the new delay has passed with the purpose of giving enough time for reverting any accidental change (i.e. using milliseconds instead of seconds) that may lock the contract. However, to avoid excessive schedules, the wait is capped by this function and it can be overridden for a custom [default\_admin\_delay](#IAccessControlDefaultAdminRules-default_admin_delay) increase scheduling. + +Make sure to add a reasonable amount of time while overriding this value, otherwise, there’s a risk of setting a high new delay that goes into effect almost immediately without the possibility of human intervention in the case of an input error (e.g. set milliseconds instead of seconds). + +#### [](#IAccessControlDefaultAdminRules-Events)Events + +#### [](#IAccessControlDefaultAdminRules-DefaultAdminTransferScheduled)`DefaultAdminTransferScheduled(new_admin: ContractAddress, accept_schedule: u64)` event + +Emitted when a `default_admin` transfer is started. + +Sets `new_admin` as the next address to become the `default_admin` by calling [accept\_default\_admin\_transfer](#IAccessControlDefaultAdminRules-accept_default_admin_transfer) only after `accept_schedule` passes. + +#### [](#IAccessControlDefaultAdminRules-DefaultAdminTransferCanceled)`DefaultAdminTransferCanceled()` event + +Emitted when a [pending\_default\_admin](#IAccessControlDefaultAdminRules-pending_default_admin) is reset if it was never accepted, regardless of its schedule. + +#### [](#IAccessControlDefaultAdminRules-DefaultAdminDelayChangeScheduled)`DefaultAdminDelayChangeScheduled(new_delay: u64, effect_schedule: u64)` event + +Emitted when a [default\_admin\_delay](#IAccessControlDefaultAdminRules-default_admin_delay) change is started. + +Sets `new_delay` as the next delay to be applied between default admins transfers after `effect_schedule` has passed. + +Emitted when a [pending\_default\_admin\_delay](#IAccessControlDefaultAdminRules-pending_default_admin_delay) is reset if its schedule didn’t pass. + +## [](#core)Core + +### [](#OwnableComponent)`OwnableComponent`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/access/src/ownable/ownable.cairo) + +```cairo +use openzeppelin_access::ownable::OwnableComponent; +``` + +`Ownable` provides a basic access control mechanism where an account (an owner) can be granted exclusive access to specific functions. + +This module includes the internal `assert_only_owner` to restrict a function to be used only by the owner. + +[Embeddable Mixin Implementations](../components#mixins) + +OwnableMixinImpl + +- [`OwnableImpl`](#OwnableComponent-Embeddable-Impls-OwnableImpl) +- [`OwnableCamelOnlyImpl`](#OwnableComponent-Embeddable-Impls-OwnableCamelOnlyImpl) + +OwnableTwoStepMixinImpl + +- [`OwnableTwoStepImpl`](#OwnableComponent-Embeddable-Impls-OwnableTwoStepImpl) +- [`OwnableTwoStepCamelOnlyImpl`](#OwnableComponent-Embeddable-Impls-OwnableTwoStepCamelOnlyImpl) + +Embeddable Implementations + +OwnableImpl + +- [`owner(self)`](#OwnableComponent-owner) +- [`transfer_ownership(self, new_owner)`](#OwnableComponent-transfer_ownership) +- [`renounce_ownership(self)`](#OwnableComponent-renounce_ownership) + +OwnableTwoStepImpl + +- [`owner(self)`](#OwnableComponent-two-step-owner) +- [`pending_owner(self)`](#OwnableComponent-two-step-pending_owner) +- [`accept_ownership(self)`](#OwnableComponent-two-step-accept_ownership) +- [`transfer_ownership(self, new_owner)`](#OwnableComponent-two-step-transfer_ownership) +- [`renounce_ownership(self)`](#OwnableComponent-two-step-renounce_ownership) + +OwnableCamelOnlyImpl + +- [`transferOwnership(self, newOwner)`](#OwnableComponent-transferOwnership) +- [`renounceOwnership(self)`](#OwnableComponent-renounceOwnership) + +OwnableTwoStepCamelOnlyImpl + +- [`pendingOwner(self)`](#OwnableComponent-two-step-pendingOwner) +- [`acceptOwnership(self)`](#OwnableComponent-two-step-acceptOwnership) +- [`transferOwnership(self, new_owner)`](#OwnableComponent-two-step-transferOwnership) +- [`renounceOwnership(self)`](#OwnableComponent-two-step-renounceOwnership) + +Internal Implementations + +InternalImpl + +- [`initializer(self, owner)`](#OwnableComponent-initializer) +- [`assert_only_owner(self)`](#OwnableComponent-assert_only_owner) +- [`_transfer_ownership(self, new_owner)`](#OwnableComponent-_transfer_ownership) +- [`_propose_owner(self, new_owner)`](#OwnableComponent-_propose_owner) + +Events + +- [`OwnershipTransferStarted(previous_owner, new_owner)`](#OwnableComponent-OwnershipTransferStarted) +- [`OwnershipTransferred(previous_owner, new_owner)`](#OwnableComponent-OwnershipTransferred) + +#### [](#OwnableComponent-Embeddable-Functions)Embeddable functions + +#### [](#OwnableComponent-owner)`owner(self: @ContractState) → ContractAddress` external + +Returns the address of the current owner. + +#### [](#OwnableComponent-transfer_ownership)`transfer_ownership(ref self: ContractState, new_owner: ContractAddress)` external + +Transfers ownership of the contract to a new account (`new_owner`). Can only be called by the current owner. + +Emits an [OwnershipTransferred](#OwnableComponent-OwnershipTransferred) event. + +#### [](#OwnableComponent-renounce_ownership)`renounce_ownership(ref self: ContractState)` external + +Leaves the contract without owner. It will not be possible to call `assert_only_owner` functions anymore. Can only be called by the current owner. + +Renouncing ownership will leave the contract without an owner, thereby removing any functionality that is only available to the owner. + +#### [](#OwnableComponent-Embeddable-Functions-Two-Step)Embeddable functions (two step transfer) + +#### [](#OwnableComponent-two-step-owner)`owner(self: @ContractState) → ContractAddress` external + +Returns the address of the current owner. + +#### [](#OwnableComponent-two-step-pending_owner)`pending_owner(self: @ContractState) → ContractAddress` external + +Returns the address of the pending owner. + +#### [](#OwnableComponent-two-step-accept_ownership)`accept_ownership(ref self: ContractState)` external + +Transfers ownership of the contract to the pending owner. Can only be called by the pending owner. Resets pending owner to zero address. + +Emits an [OwnershipTransferred](#OwnableComponent-OwnershipTransferred) event. + +#### [](#OwnableComponent-two-step-transfer_ownership)`transfer_ownership(ref self: ContractState, new_owner: ContractAddress)` external + +Starts the two step ownership transfer process, by setting the pending owner. Setting `new_owner` to the zero address is allowed, this can be used to cancel an initiated ownership transfer. + +Can only be called by the current owner. + +Emits an [OwnershipTransferStarted](#OwnableComponent-OwnershipTransferStarted) event. + +#### [](#OwnableComponent-two-step-renounce_ownership)`renounce_ownership(ref self: ContractState)` external + +Leaves the contract without owner. It will not be possible to call `assert_only_owner` functions anymore. Can only be called by the current owner. + +Renouncing ownership will leave the contract without an owner, thereby removing any functionality that is only available to the owner. + +#### [](#OwnableComponent-transferOwnership)`transferOwnership(ref self: ContractState, newOwner: ContractAddress)` external + +See [transfer\_ownership](#OwnableComponent-transfer_ownership). + +#### [](#OwnableComponent-renounceOwnership)`renounceOwnership(ref self: ContractState)` external + +See [renounce\_ownership](#OwnableComponent-renounce_ownership). + +#### [](#OwnableComponent-two-step-pendingOwner)`pendingOwner(self: @ContractState)` external + +See [pending\_owner](#OwnableComponent-two-step-pending_owner). + +#### [](#OwnableComponent-two-step-acceptOwnership)`acceptOwnership(self: @ContractState)` external + +See [accept\_ownership](#OwnableComponent-two-step-accept_ownership). + +#### [](#OwnableComponent-two-step-transferOwnership)`transferOwnership(self: @ContractState)` external + +See [transfer\_ownership](#OwnableComponent-two-step-transfer_ownership). + +#### [](#OwnableComponent-two-step-renounceOwnership)`renounceOwnership(self: @ContractState)` external + +See [renounce\_ownership](#OwnableComponent-two-step-renounce_ownership). + +#### [](#OwnableComponent-Internal-Functions)Internal functions + +#### [](#OwnableComponent-initializer)`initializer(ref self: ContractState, owner: ContractAddress)` internal + +Initializes the contract and sets `owner` as the initial owner. + +Requirements: + +- `owner` cannot be the zero address. + +Emits an [OwnershipTransferred](#OwnableComponent-OwnershipTransferred) event. + +#### [](#OwnableComponent-assert_only_owner)`assert_only_owner(self: @ContractState)` internal + +Panics if called by any account other than the owner. + +#### [](#OwnableComponent-_transfer_ownership)`_transfer_ownership(ref self: ContractState, new_owner: ContractAddress)` internal + +Transfers ownership of the contract to a new account (`new_owner`). Internal function without access restriction. + +Emits an [OwnershipTransferred](#OwnableComponent-OwnershipTransferred) event. + +#### [](#OwnableComponent-_propose_owner)`_propose_owner(ref self: ContractState, new_owner: ContractAddress)` internal + +Sets a new pending owner in a two step transfer. + +Internal function without access restriction. + +Emits an [OwnershipTransferStarted](#OwnableComponent-OwnershipTransferStarted) event. + +#### [](#OwnableComponent-Events)Events + +#### [](#OwnableComponent-OwnershipTransferStarted)`OwnershipTransferStarted(previous_owner: ContractAddress, new_owner: ContractAddress)` event + +Emitted when the pending owner is updated. + +#### [](#OwnableComponent-OwnershipTransferred)`OwnershipTransferred(previous_owner: ContractAddress, new_owner: ContractAddress)` event + +Emitted when the ownership is transferred. + +### [](#AccessControlComponent)`AccessControlComponent`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/access/src/accesscontrol/accesscontrol.cairo) + +```cairo +use openzeppelin_access::accesscontrol::AccessControlComponent; +``` + +Component that allows contracts to implement role-based access control mechanisms. Roles are referred to by their `felt252` identifier: + +```cairo +const MY_ROLE: felt252 = selector!("MY_ROLE"); +``` + +Roles can be used to represent a set of permissions. To restrict access to a function call, use [`assert_only_role`](#AccessControlComponent-assert_only_role): + +```cairo +(...) + +#[external(v0)] +fn foo(ref self: ContractState) { + self.accesscontrol.assert_only_role(MY_ROLE); + + // Do something +} +``` + +Roles can be granted and revoked dynamically via the [grant\_role](#AccessControlComponent-grant_role), [grant\_role\_with\_delay](#IAccessControlWithDelay-grant_role_with_delay) and [revoke\_role](#AccessControlComponent-revoke_role) functions. Each role has an associated admin role, and only accounts that have a role’s admin role can call [grant\_role](#AccessControlComponent-grant_role), [grant\_role\_with\_delay](#IAccessControlWithDelay-grant_role_with_delay) and [revoke\_role](#AccessControlComponent-revoke_role). + +By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means that only accounts with this role will be able to grant or revoke other roles. More complex role relationships can be created by using [set\_role\_admin](#AccessControlComponent-set_role_admin). + +The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to grant and revoke this role. Extra precautions should be taken to secure accounts that have been granted it. See [AccessControlDefaultAdminRulesComponent](#AccessControlDefaultAdminRulesComponent). + +[Embeddable Mixin Implementations](../components#mixins) + +AccessControlMixinImpl + +- [`AccessControlImpl`](#AccessControlComponent-Embeddable-Impls-AccessControlImpl) +- [`AccessControlCamelImpl`](#AccessControlComponent-Embeddable-Impls-AccessControlCamelImpl) +- [`AccessControlWithDelayImpl`](#AccessControlComponent-Embeddable-Impls-AccessControlWithDelayImpl) +- [`SRC5Impl`](introspection#SRC5Component-Embeddable-Impls) + +Embeddable Implementations + +AccessControlImpl + +- [`has_role(self, role, account)`](#AccessControlComponent-has_role) +- [`get_role_admin(self, role)`](#AccessControlComponent-get_role_admin) +- [`grant_role(self, role, account)`](#AccessControlComponent-grant_role) +- [`revoke_role(self, role, account)`](#AccessControlComponent-revoke_role) +- [`renounce_role(self, role, account)`](#AccessControlComponent-renounce_role) + +AccessControlCamelImpl + +- [`hasRole(self, role, account)`](#AccessControlComponent-hasRole) +- [`getRoleAdmin(self, role)`](#AccessControlComponent-getRoleAdmin) +- [`grantRole(self, role, account)`](#AccessControlComponent-grantRole) +- [`revokeRole(self, role, account)`](#AccessControlComponent-revokeRole) +- [`renounceRole(self, role, account)`](#AccessControlComponent-renounceRole) + +AccessControlWithDelayImpl + +- [`get_role_status(self, role, account)`](#AccessControlComponent-get_role_status) +- [`grant_role_with_delay(self, role, account, delay)`](#AccessControlComponent-grant_role_with_delay) + +SRC5Impl + +- [`supports_interface(self, interface_id: felt252)`](introspection#ISRC5-supports_interface) + +Internal Implementations + +InternalImpl + +- [`initializer(self)`](#AccessControlComponent-initializer) +- [`assert_only_role(self, role)`](#AccessControlComponent-assert_only_role) +- [`is_role_effective(self, role, account)`](#AccessControlComponent-is_role_effective) +- [`resolve_role_status(self, role, account)`](#AccessControlComponent-resolve_role_status) +- [`is_role_granted(self, role, account)`](#AccessControlComponent-is_role_granted) +- [`set_role_admin(self, role, admin_role)`](#AccessControlComponent-set_role_admin) +- [`_grant_role(self, role, account)`](#AccessControlComponent-_grant_role) +- [`_grant_role_with_delay(self, role, account, delay)`](#AccessControlComponent-_grant_role_with_delay) +- [`_revoke_role(self, role, account)`](#AccessControlComponent-_revoke_role) + +Events + +IAccessControl + +- [`RoleAdminChanged(role, previous_admin_role, new_admin_role)`](#AccessControlComponent-RoleAdminChanged) +- [`RoleGranted(role, account, sender)`](#AccessControlComponent-RoleGranted) +- [`RoleRevoked(role, account, sender)`](#AccessControlComponent-RoleRevoked) + +IAccessControlWithDelay + +- [`RoleGrantedWithDelay(role, account, sender, delay)`](#AccessControlComponent-RoleGrantedWithDelay) + +#### [](#AccessControlComponent-Embeddable-Functions)Embeddable functions + +#### [](#AccessControlComponent-has_role)`has_role(self: @ContractState, role: felt252, account: ContractAddress) → bool` external + +Returns whether `account` can act as `role`. + +#### [](#AccessControlComponent-get_role_admin)`get_role_admin(self: @ContractState, role: felt252) → felt252` external + +Returns the admin role that controls `role`. See [grant\_role](#AccessControlComponent-grant_role) and [revoke\_role](#AccessControlComponent-revoke_role). + +To change a role’s admin, use [set\_role\_admin](#AccessControlComponent-set_role_admin). + +#### [](#AccessControlComponent-get_role_status)`get_role_status(self: @ContractState, role: felt252, account: ContractAddress) → RoleStatus` external + +Returns the account’s status for the given role. + +The possible statuses are: + +- `NotGranted`: the role has not been granted to the account. +- `Delayed`: The role has been granted to the account but is not yet active due to a time delay. +- `Effective`: the role has been granted to the account and is currently active. + +#### [](#AccessControlComponent-grant_role)`grant_role(ref self: ContractState, role: felt252, account: ContractAddress)` external + +Grants `role` to `account`. + +If `account` had not been already granted `role`, emits a [RoleGranted](#IAccessControl-RoleGranted) event. + +Requirements: + +- the caller must have `role`'s admin role. + +May emit a [RoleGranted](#IAccessControl-RoleGranted) event. + +#### [](#AccessControlComponent-grant_role_with_delay)`grant_role_with_delay(ref self: ContractState, role: felt252, account: ContractAddress, delay: u64)` external + +Attempts to grant `role` to `account` with the specified activation delay. + +Requirements: + +- The caller must have \`role’s admin role. +- delay must be greater than 0. +- the `role` must not be already effective for `account`. + +May emit a [RoleGrantedWithDelay](#IAccessControlWithDelay-RoleGrantedWithDelay) event. + +#### [](#AccessControlComponent-revoke_role)`revoke_role(ref self: ContractState, role: felt252, account: ContractAddress)` external + +Revokes `role` from `account`. + +If `account` had been granted `role`, emits a [RoleRevoked](#IAccessControl-RoleRevoked) event. + +Requirements: + +- the caller must have `role`'s admin role. + +May emit a [RoleRevoked](#IAccessControl-RoleRevoked) event. + +#### [](#AccessControlComponent-renounce_role)`renounce_role(ref self: ContractState, role: felt252, account: ContractAddress)` external + +Revokes `role` from the calling account. + +Roles are often managed via [grant\_role](#AccessControlComponent-grant_role) and [revoke\_role](#AccessControlComponent-revoke_role). This function’s purpose is to provide a mechanism for accounts to lose their privileges if they are compromised (such as when a trusted device is misplaced). + +If the calling account had been revoked `role`, emits a [RoleRevoked](#IAccessControl-RoleRevoked) event. + +Requirements: + +- the caller must be `account`. + +May emit a [RoleRevoked](#IAccessControl-RoleRevoked) event. + +#### [](#AccessControlComponent-supports_interface)`supports_interface(self: @ContractState, interface_id: felt252) → bool` external + +See [ISRC5::supports\_interface](introspection#ISRC5-supports_interface). + +#### [](#AccessControlComponent-hasRole)`hasRole(self: @ContractState, role: felt252, account: ContractAddress) → bool` external + +See [has\_role](#AccessControlComponent-has_role). + +#### [](#AccessControlComponent-getRoleAdmin)`getRoleAdmin(self: @ContractState, role: felt252) → felt252` external + +See [get\_role\_admin](#AccessControlComponent-get_role_admin). + +#### [](#AccessControlComponent-grantRole)`grantRole(ref self: ContractState, role: felt252, account: ContractAddress)` external + +See [grant\_role](#AccessControlComponent-grant_role). + +#### [](#AccessControlComponent-revokeRole)`revokeRole(ref self: ContractState, role: felt252, account: ContractAddress)` external + +See [revoke\_role](#AccessControlComponent-revoke_role). + +#### [](#AccessControlComponent-renounceRole)`renounceRole(ref self: ContractState, role: felt252, account: ContractAddress)` external + +See [renounce\_role](#AccessControlComponent-renounce_role). + +#### [](#AccessControlComponent-Internal-Functions)Internal functions + +#### [](#AccessControlComponent-initializer)`initializer(ref self: ContractState)` internal + +Initializes the contract by registering the [IAccessControl](#IAccessControl) interface ID. + +#### [](#AccessControlComponent-assert_only_role)`assert_only_role(self: @ContractState, role: felt252)` internal + +Validates that the caller can act as the given role. Otherwise it panics. + +#### [](#AccessControlComponent-is_role_effective)`is_role_effective(self: @ContractState, role: felt252, account: ContractAddress) → bool` internal + +Returns whether the account can act as the given role. + +The account can act as the role if it is active and the `effective_from` time is before or equal to the current time. + +If the `effective_from` timepoint is 0, the role is effective immediately. This is backwards compatible with implementations that didn’t use delays but a single boolean flag. + +#### [](#AccessControlComponent-resolve_role_status)`resolve_role_status(self: @ContractState, role: felt252, account: ContractAddress) → RoleStatus` internal + +Returns the account’s status for the given role. + +The possible statuses are: + +- `NotGranted`: the role has not been granted to the account. +- `Delayed`: The role has been granted to the account but is not yet active due to a time delay. +- `Effective`: the role has been granted to the account and is currently active. + +#### [](#AccessControlComponent-is_role_granted)`is_role_granted(self: @ContractState, role: felt252, account: ContractAddress) → bool` internal + +Returns whether the account has the given role granted. + +The account may not be able to act as the role yet, if a delay was set and has not passed yet. Use `is_role_effective` to check if the account can act as the role. + +#### [](#AccessControlComponent-set_role_admin)`set_role_admin(ref self: ContractState, role: felt252, admin_role: felt252)` internal + +Sets `admin_role` as `role`'s admin role. + +Internal function without access restriction. + +Emits a [RoleAdminChanged](#IAccessControl-RoleAdminChanged) event. + +#### [](#AccessControlComponent-_grant_role)`_grant_role(ref self: ContractState, role: felt252, account: ContractAddress)` internal + +Attempts to grant `role` to `account`. The function does nothing if `role` is already effective for `account`. If `role` has been granted to `account`, but is not yet active due to a time delay, the delay is removed and `role` becomes effective immediately. + +Internal function without access restriction. + +May emit a [RoleGranted](#IAccessControl-RoleGranted) event. + +#### [](#AccessControlComponent-_grant_role_with_delay)`_grant_role_with_delay(ref self: ContractState, role: felt252, account: ContractAddress, delay: u64)` internal + +Attempts to grant `role` to `account` with the specified activation delay. + +The role will become effective after the given delay has passed. If the role is already active (`Effective`) for the account, the function will panic. If the role has been granted but is not yet active (being in the `Delayed` state), the existing delay will be overwritten with the new `delay`. + +Internal function without access restriction. + +Requirements: + +- delay must be greater than 0. +- the `role` must not be already effective for `account`. + +May emit a [RoleGrantedWithDelay](#IAccessControlWithDelay-RoleGrantedWithDelay) event. + +#### [](#AccessControlComponent-_revoke_role)`_revoke_role(ref self: ContractState, role: felt252, account: ContractAddress)` internal + +Revokes `role` from `account`. + +Internal function without access restriction. + +May emit a [RoleRevoked](#IAccessControl-RoleRevoked) event. + +#### [](#AccessControlComponent-Events)Events + +#### [](#AccessControlComponent-RoleAdminChanged)`RoleAdminChanged(role: felt252, previous_admin_role: ContractAddress, new_admin_role: ContractAddress)` event + +See [IAccessControl::RoleAdminChanged](#IAccessControl-RoleAdminChanged). + +#### [](#AccessControlComponent-RoleGranted)`RoleGranted(role: felt252, account: ContractAddress, sender: ContractAddress)` event + +See [IAccessControl::RoleGranted](#IAccessControl-RoleGranted). + +#### [](#AccessControlComponent-RoleGrantedWithDelay)`RoleGrantedWithDelay(role: felt252, account: ContractAddress, sender: ContractAddress, delay: u64)` event + +See [IAccessControlWithDelay::RoleGrantedWithDelay](#IAccessControlWithDelay-RoleGrantedWithDelay). + +#### [](#AccessControlComponent-RoleRevoked)`RoleRevoked(role: felt252, account: ContractAddress, sender: ContractAddress)` event + +See [IAccessControl::RoleRevoked](#IAccessControl-RoleRevoked). + +## [](#extensions)Extensions + +### [](#AccessControlDefaultAdminRulesComponent)`AccessControlDefaultAdminRulesComponent`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/access/src/accesscontrol/extensions/accesscontrol_default_admin_rules.cairo) + +```cairo +use openzeppelin_access::accesscontrol::extensions::AccessControlDefaultAdminRulesComponent; +``` + +Extension of [AccessControl](#AccessControlComponent) that allows specifying special rules to manage the `DEFAULT_ADMIN_ROLE` holder, which is a sensitive role with special permissions over other roles that may potentially have privileged rights in the system. + +If a specific role doesn’t have an admin role assigned, the holder of the `DEFAULT_ADMIN_ROLE` will have the ability to grant it and revoke it. + +This contract implements the following risk mitigations on top of [AccessControl](#AccessControlComponent): + +- Only one account holds the `DEFAULT_ADMIN_ROLE` since deployment until it’s potentially renounced. +- Enforces a 2-step process to transfer the `DEFAULT_ADMIN_ROLE` to another account. +- Enforces a configurable delay between the two steps, with the ability to cancel before the transfer is accepted. +- The delay can be changed by scheduling, see [change\_default\_admin\_delay](#IAccessControlDefaultAdminRules-change_default_admin_delay). +- It is not possible to use another role to manage the `DEFAULT_ADMIN_ROLE`. + +[Embeddable Mixin Implementations](../components#mixins) + +AccessControlMixinImpl + +- [`AccessControlDefaultAdminRulesImpl`](#AccessControlDefaultAdminRulesComponent-Embeddable-Impls-AccessControlDefaultAdminRulesImpl) +- [`AccessControlImpl`](#AccessControlDefaultAdminRulesComponent-Embeddable-Impls-AccessControlImpl) +- [`AccessControlCamelImpl`](#AccessControlDefaultAdminRulesComponent-Embeddable-Impls-AccessControlCamelImpl) +- [`AccessControlWithDelayImpl`](#AccessControlDefaultAdminRulesComponent-Embeddable-Impls-AccessControlWithDelayImpl) +- [`SRC5Impl`](introspection#SRC5Component-Embeddable-Impls) + +Embeddable Implementations + +AccessControlDefaultAdminRulesImpl + +- [`default_admin(self)`](#IAccessControlDefaultAdminRules-default_admin) +- [`pending_default_admin(self)`](#IAccessControlDefaultAdminRules-pending_default_admin) +- [`default_admin_delay(self)`](#IAccessControlDefaultAdminRules-default_admin_delay) +- [`pending_default_admin_delay(self)`](#IAccessControlDefaultAdminRules-pending_default_admin_delay) +- [`begin_default_admin_transfer(self, new_admin)`](#IAccessControlDefaultAdminRules-begin_default_admin_transfer) +- [`cancel_default_admin_transfer(self)`](#IAccessControlDefaultAdminRules-cancel_default_admin_transfer) +- [`accept_default_admin_transfer(self)`](#IAccessControlDefaultAdminRules-accept_default_admin_transfer) +- [`change_default_admin_delay(self, new_delay)`](#IAccessControlDefaultAdminRules-change_default_admin_delay) +- [`rollback_default_admin_delay(self)`](#IAccessControlDefaultAdminRules-rollback_default_admin_delay) +- [`default_admin_delay_increase_wait(self)`](#IAccessControlDefaultAdminRules-default_admin_delay_increase_wait) + +AccessControlImpl + +- [`has_role(self, role, account)`](#AccessControlDefaultAdminRulesComponent-has_role) +- [`get_role_admin(self, role)`](#AccessControlDefaultAdminRulesComponent-get_role_admin) +- [`grant_role(self, role, account)`](#AccessControlDefaultAdminRulesComponent-grant_role) +- [`revoke_role(self, role, account)`](#AccessControlDefaultAdminRulesComponent-revoke_role) +- [`renounce_role(self, role, account)`](#AccessControlDefaultAdminRulesComponent-renounce_role) + +AccessControlCamelImpl + +- [`hasRole(self, role, account)`](#AccessControlDefaultAdminRulesComponent-hasRole) +- [`getRoleAdmin(self, role)`](#AccessControlDefaultAdminRulesComponent-getRoleAdmin) +- [`grantRole(self, role, account)`](#AccessControlDefaultAdminRulesComponent-grantRole) +- [`revokeRole(self, role, account)`](#AccessControlDefaultAdminRulesComponent-revokeRole) +- [`renounceRole(self, role, account)`](#AccessControlDefaultAdminRulesComponent-renounceRole) + +AccessControlWithDelayImpl + +- [`get_role_status(self, role, account)`](#AccessControlDefaultAdminRulesComponent-get_role_status) +- [`grant_role_with_delay(self, role, account, delay)`](#AccessControlDefaultAdminRulesComponent-grant_role_with_delay) + +SRC5Impl + +- [`supports_interface(self, interface_id: felt252)`](introspection#ISRC5-supports_interface) + +Internal Implementations + +InternalImpl + +- [`initializer(self, initial_delay, initial_default_admin)`](#AccessControlDefaultAdminRulesComponent-initializer) +- [`assert_only_role(self, role)`](#AccessControlDefaultAdminRulesComponent-assert_only_role) +- [`is_role_effective(self, role, account)`](#AccessControlDefaultAdminRulesComponent-is_role_effective) +- [`resolve_role_status(self, role, account)`](#AccessControlDefaultAdminRulesComponent-resolve_role_status) +- [`is_role_granted(self, role, account)`](#AccessControlDefaultAdminRulesComponent-is_role_granted) +- [`set_role_admin(self, role, admin_role)`](#AccessControlDefaultAdminRulesComponent-set_role_admin) +- [`_grant_role(self, role, account)`](#AccessControlDefaultAdminRulesComponent-_grant_role) +- [`_grant_role_with_delay(self, role, account, delay)`](#AccessControlDefaultAdminRulesComponent-_grant_role_with_delay) +- [`_revoke_role(self, role, account)`](#AccessControlDefaultAdminRulesComponent-_revoke_role) +- [`set_pending_default_admin(self, new_admin, new_schedule)`](#AccessControlDefaultAdminRulesComponent-set_pending_default_admin) +- [`set_pending_delay(self, new_delay, new_schedule)`](#AccessControlDefaultAdminRulesComponent-set_pending_delay) +- [`delay_change_wait(self, new_delay)`](#AccessControlDefaultAdminRulesComponent-delay_change_wait) + +Events + +IAccessControl + +- [`RoleAdminChanged(role, previous_admin_role, new_admin_role)`](#AccessControlDefaultAdminRulesComponent-RoleAdminChanged) +- [`RoleGranted(role, account, sender)`](#AccessControlDefaultAdminRulesComponent-RoleGranted) +- [`RoleRevoked(role, account, sender)`](#AccessControlDefaultAdminRulesComponent-RoleRevoked) + +IAccessControlWithDelay + +- [`RoleGrantedWithDelay(role, account, sender, delay)`](#AccessControlDefaultAdminRulesComponent-RoleGrantedWithDelay) + +IAccessControlDefaultAdminRules + +- [`DefaultAdminTransferScheduled(new_admin, accept_schedule)`](#AccessControlDefaultAdminRulesComponent-DefaultAdminTransferScheduled) +- [`DefaultAdminTransferCanceled()`](#AccessControlDefaultAdminRulesComponent-DefaultAdminTransferCanceled) +- [`DefaultAdminDelayChangeScheduled(new_delay, effect_schedule)`](#AccessControlDefaultAdminRulesComponent-DefaultAdminDelayChangeScheduled) +- [`DefaultAdminDelayChangeCanceled()`](#AccessControlDefaultAdminRulesComponent-DefaultAdminDelayChangeCanceled) + +#### [](#AccessControlDefaultAdminRulesComponent-Embeddable-Functions)Embeddable functions + +#### [](#AccessControlDefaultAdminRulesComponent-default_admin)`default_admin(self: @ContractState) → ContractAddress` external + +Returns the address of the current `DEFAULT_ADMIN_ROLE` holder. + +#### [](#AccessControlDefaultAdminRulesComponent-pending_default_admin)`pending_default_admin(self: @ContractState) → (ContractAddress, u64)` external + +Returns a tuple of a `new_admin` and an `accept_schedule`. + +After the `accept_schedule` passes, the `new_admin` will be able to accept the `default_admin` role by calling [accept\_default\_admin\_transfer](#AccessControlDefaultAdminRulesComponent-accept_default_admin_transfer), completing the role transfer. + +A zero value only in `accept_schedule` indicates no pending admin transfer. + +A zero address `new_admin` means that `default_admin` is being renounced. + +#### [](#AccessControlDefaultAdminRulesComponent-default_admin_delay)`default_admin_delay(self: @ContractState) → u64` external + +Returns the delay required to schedule the acceptance of a `default_admin` transfer started. + +This delay will be added to the current timestamp when calling [begin\_default\_admin\_transfer](#AccessControlDefaultAdminRulesComponent-begin_default_admin_transfer) to set the acceptance schedule. + +If a delay change has been scheduled, it will take effect as soon as the schedule passes, making this function returns the new delay. + +See [change\_default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-change_default_admin_delay). + +#### [](#AccessControlDefaultAdminRulesComponent-pending_default_admin_delay)`pending_default_admin_delay(self: @ContractState) → (u64, u64)` external + +Returns a tuple of `new_delay` and an `effect_schedule`. + +After the `effect_schedule` passes, the `new_delay` will get into effect immediately for every new `default_admin` transfer started with [begin\_default\_admin\_transfer](#AccessControlDefaultAdminRulesComponent-begin_default_admin_transfer). + +A zero value only in `effect_schedule` indicates no pending delay change. + +A zero value only for `new_delay` means that the next [default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-default_admin_delay) will be zero after the effect schedule. + +#### [](#AccessControlDefaultAdminRulesComponent-begin_default_admin_transfer)`begin_default_admin_transfer(ref self: ContractState, new_admin: ContractAddress)` external + +Starts a `default_admin` transfer by setting a [pending\_default\_admin](#AccessControlDefaultAdminRulesComponent-pending_default_admin) scheduled for acceptance after the current timestamp plus a [default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-default_admin_delay). + +Requirements: + +- Only can be called by the current `default_admin`. + +Emits a [DefaultAdminTransferScheduled](#AccessControlDefaultAdminRulesComponent-DefaultAdminTransferScheduled) event. + +#### [](#AccessControlDefaultAdminRulesComponent-cancel_default_admin_transfer)`cancel_default_admin_transfer(ref self: ContractState)` external + +Cancels a `default_admin` transfer previously started with [begin\_default\_admin\_transfer](#AccessControlDefaultAdminRulesComponent-begin_default_admin_transfer). + +A [pending\_default\_admin](#AccessControlDefaultAdminRulesComponent-pending_default_admin) not yet accepted can also be cancelled with this function. + +Requirements: + +- Only can be called by the current `default_admin`. + +May emit a [DefaultAdminTransferCanceled](#AccessControlDefaultAdminRulesComponent-DefaultAdminTransferCanceled) event. + +#### [](#AccessControlDefaultAdminRulesComponent-accept_default_admin_transfer)`accept_default_admin_transfer(ref self: ContractState)` external + +Completes a `default_admin` transfer previously started with [begin\_default\_admin\_transfer](#AccessControlDefaultAdminRulesComponent-begin_default_admin_transfer). + +After calling the function: + +- `DEFAULT_ADMIN_ROLE` must be granted to the caller. +- `DEFAULT_ADMIN_ROLE` must be revoked from the previous holder. +- [pending\_default\_admin](#AccessControlDefaultAdminRulesComponent-pending_default_admin) must be reset to zero values. + +Requirements: + +- Only can be called by the [pending\_default\_admin](#AccessControlDefaultAdminRulesComponent-pending_default_admin)'s `new_admin`. +- The [pending\_default\_admin](#AccessControlDefaultAdminRulesComponent-pending_default_admin)'s `accept_schedule` should’ve passed. + +#### [](#AccessControlDefaultAdminRulesComponent-change_default_admin_delay)`change_default_admin_delay(ref self: ContractState, new_delay: u64)` external + +Initiates a [default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-default_admin_delay) update by setting a [pending\_default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-pending_default_admin_delay) scheduled for getting into effect after the current timestamp plus a [default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-default_admin_delay). + +This function guarantees that any call to [begin\_default\_admin\_transfer](#AccessControlDefaultAdminRulesComponent-begin_default_admin_transfer) done between the timestamp this method is called and the [pending\_default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-pending_default_admin_delay) effect schedule will use the current [default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-default_admin_delay) set before calling. + +The [pending\_default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-pending_default_admin_delay)'s effect schedule is defined in a way that waiting until the schedule and then calling [begin\_default\_admin\_transfer](#AccessControlDefaultAdminRulesComponent-begin_default_admin_transfer) with the new delay will take at least the same as another `default_admin` complete transfer (including acceptance). + +The schedule is designed for two scenarios: + +- When the delay is changed for a larger one the schedule is `block.timestamp new delay` capped by [default\_admin\_delay\_increase\_wait](#AccessControlDefaultAdminRulesComponent-default_admin_delay_increase_wait). +- When the delay is changed for a shorter one, the schedule is `block.timestamp (current delay - new delay)`. + +A [pending\_default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-pending_default_admin_delay) that never got into effect will be canceled in favor of a new scheduled change. + +Requirements: + +- Only can be called by the current `default_admin`. + +Emits a [DefaultAdminDelayChangeScheduled](#AccessControlDefaultAdminRulesComponent-DefaultAdminDelayChangeScheduled) event and may emit a [DefaultAdminDelayChangeCanceled](#AccessControlDefaultAdminRulesComponent-DefaultAdminDelayChangeCanceled) event. + +#### [](#AccessControlDefaultAdminRulesComponent-rollback_default_admin_delay)`rollback_default_admin_delay(ref self: ContractState)` external + +Cancels a scheduled [default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-default_admin_delay) change. + +Requirements: + +- Only can be called by the current `default_admin`. + +May emit a [DefaultAdminDelayChangeCanceled](#AccessControlDefaultAdminRulesComponent-DefaultAdminDelayChangeCanceled) event. + +#### [](#AccessControlDefaultAdminRulesComponent-default_admin_delay_increase_wait)`default_admin_delay_increase_wait(self: @ContractState) → u64` external + +Maximum time in seconds for an increase to [default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-default_admin_delay) (that is scheduled using [change\_default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-change_default_admin_delay)) to take effect. Defaults to 5 days. + +When the [default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-default_admin_delay) is scheduled to be increased, it goes into effect after the new delay has passed with the purpose of giving enough time for reverting any accidental change (i.e. using milliseconds instead of seconds) that may lock the contract. However, to avoid excessive schedules, the wait is capped by this function and it can be overridden for a custom [default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-default_admin_delay) increase scheduling. + +Make sure to add a reasonable amount of time while overriding this value, otherwise, there’s a risk of setting a high new delay that goes into effect almost immediately without the possibility of human intervention in the case of an input error (eg. set milliseconds instead of seconds). + +#### [](#AccessControlDefaultAdminRulesComponent-has_role)`has_role(self: @ContractState, role: felt252, account: ContractAddress) → bool` external + +Returns whether `account` can act as `role`. + +#### [](#AccessControlDefaultAdminRulesComponent-get_role_admin)`get_role_admin(self: @ContractState, role: felt252) → felt252` external + +Returns the admin role that controls `role`. See [grant\_role](#AccessControlComponent-grant_role) and [revoke\_role](#AccessControlComponent-revoke_role). + +To change a role’s admin, use [set\_role\_admin](#AccessControlComponent-set_role_admin). + +#### [](#AccessControlDefaultAdminRulesComponent-get_role_status)`get_role_status(self: @ContractState, role: felt252, account: ContractAddress) → RoleStatus` external + +Returns the account’s status for the given role. + +The possible statuses are: + +- `NotGranted`: the role has not been granted to the account. +- `Delayed`: The role has been granted to the account but is not yet active due to a time delay. +- `Effective`: the role has been granted to the account and is currently active. + +#### [](#AccessControlDefaultAdminRulesComponent-grant_role)`grant_role(ref self: ContractState, role: felt252, account: ContractAddress)` external + +Grants `role` to `account`. + +If `account` had not been already granted `role`, emits a [RoleGranted](#AccessControlDefaultAdminRulesComponent-RoleGranted) event. + +Requirements: + +- the caller must have `role`'s admin role. + +May emit a [RoleGranted](#AccessControlDefaultAdminRulesComponent-RoleGranted) event. + +#### [](#AccessControlDefaultAdminRulesComponent-grant_role_with_delay)`grant_role_with_delay(ref self: ContractState, role: felt252, account: ContractAddress, delay: u64)` external + +Attempts to grant `role` to `account` with the specified activation delay. + +Requirements: + +- The caller must have \`role’s admin role. +- delay must be greater than 0. +- the `role` must not be already effective for `account`. + +May emit a [RoleGrantedWithDelay](#AccessControlDefaultAdminRulesComponent-RoleGrantedWithDelay) event. + +#### [](#AccessControlDefaultAdminRulesComponent-revoke_role)`revoke_role(ref self: ContractState, role: felt252, account: ContractAddress)` external + +Revokes `role` from `account`. + +If `account` had been granted `role`, emits a [RoleRevoked](#AccessControlDefaultAdminRulesComponent-RoleRevoked) event. + +Requirements: + +- the caller must have `role`'s admin role. + +May emit a [RoleRevoked](#AccessControlDefaultAdminRulesComponent-RoleRevoked) event. + +#### [](#AccessControlDefaultAdminRulesComponent-renounce_role)`renounce_role(ref self: ContractState, role: felt252, account: ContractAddress)` external + +Revokes `role` from the calling account. + +Roles are often managed via [grant\_role](#AccessControlComponent-grant_role) and [revoke\_role](#AccessControlComponent-revoke_role). This function’s purpose is to provide a mechanism for accounts to lose their privileges if they are compromised (such as when a trusted device is misplaced). + +If the calling account had been revoked `role`, emits a [RoleRevoked](#AccessControlDefaultAdminRulesComponent-RoleRevoked) event. + +Requirements: + +- the caller must be `account`. + +May emit a [RoleRevoked](#AccessControlDefaultAdminRulesComponent-RoleRevoked) event. + +#### [](#AccessControlDefaultAdminRulesComponent-supports_interface)`supports_interface(self: @ContractState, interface_id: felt252) → bool` external + +See [ISRC5::supports\_interface](introspection#ISRC5-supports_interface). + +#### [](#AccessControlDefaultAdminRulesComponent-hasRole)`hasRole(self: @ContractState, role: felt252, account: ContractAddress) → bool` external + +See [has\_role](#AccessControlDefaultAdminRulesComponent-has_role). + +#### [](#AccessControlDefaultAdminRulesComponent-getRoleAdmin)`getRoleAdmin(self: @ContractState, role: felt252) → felt252` external + +See [get\_role\_admin](#AccessControlDefaultAdminRulesComponent-get_role_admin). + +#### [](#AccessControlDefaultAdminRulesComponent-grantRole)`grantRole(ref self: ContractState, role: felt252, account: ContractAddress)` external + +See [grant\_role](#AccessControlDefaultAdminRulesComponent-grant_role). + +#### [](#AccessControlDefaultAdminRulesComponent-revokeRole)`revokeRole(ref self: ContractState, role: felt252, account: ContractAddress)` external + +See [revoke\_role](#AccessControlDefaultAdminRulesComponent-revoke_role). + +#### [](#AccessControlDefaultAdminRulesComponent-renounceRole)`renounceRole(ref self: ContractState, role: felt252, account: ContractAddress)` external + +See [renounce\_role](#AccessControlDefaultAdminRulesComponent-renounce_role). + +#### [](#AccessControlDefaultAdminRulesComponent-Internal-Functions)Internal functions + +#### [](#AccessControlDefaultAdminRulesComponent-initializer)`initializer(ref self: ContractState, initial_delay: u64, initial_default_admin: ContractAddress)` external + +Initializes the contract by registering the IAccessControl interface ID and setting the initial delay and default admin. + +Requirements: + +- `initial_default_admin` must not be the zero address. + +#### [](#AccessControlDefaultAdminRulesComponent-assert_only_role)`assert_only_role(self: @ContractState, role: felt252)` external + +Validates that the caller can act as the given role. Otherwise it panics. + +#### [](#AccessControlDefaultAdminRulesComponent-is_role_effective)`is_role_effective(self: @ContractState, role: felt252, account: ContractAddress) → bool` external + +Returns whether the account can act as the given role. + +The account can act as the role if it is active and the `effective_from` time is before or equal to the current time. + +If the `effective_from` timepoint is 0, the role is effective immediately. This is backwards compatible with implementations that didn’t use delays but a single boolean flag. + +#### [](#AccessControlDefaultAdminRulesComponent-resolve_role_status)`resolve_role_status(self: @ContractState, role: felt252, account: ContractAddress) → RoleStatus` external + +Returns the account’s status for the given role. + +The possible statuses are: + +- `NotGranted`: the role has not been granted to the account. +- `Delayed`: The role has been granted to the account but is not yet active due to a time delay. +- `Effective`: the role has been granted to the account and is currently active. + +#### [](#AccessControlDefaultAdminRulesComponent-is_role_granted)`is_role_granted(self: @ContractState, role: felt252, account: ContractAddress) → bool` external + +Returns whether the account has the given role granted. + +The account may not be able to act as the role yet, if a delay was set and has not passed yet. Use is\_role\_effective to check if the account can act as the role. + +#### [](#AccessControlDefaultAdminRulesComponent-set_role_admin)`set_role_admin(ref self: ContractState, role: felt252, admin_role: felt252)` external + +Sets `admin_role` as \`role’s admin role. + +Internal function without access restriction. + +Requirements: + +- `role` must not be `DEFAULT_ADMIN_ROLE`. + +Emits a [RoleAdminChanged](#AccessControlDefaultAdminRulesComponent-RoleAdminChanged) event. + +#### [](#AccessControlDefaultAdminRulesComponent-_grant_role)`_grant_role(ref self: ContractState, role: felt252, account: ContractAddress)` external + +Attempts to grant `role` to `account`. The function does nothing if `role` is already effective for `account`. If `role` has been granted to `account`, but is not yet active due to a time delay, the delay is removed and `role` becomes effective immediately. + +Internal function without access restriction. + +For `DEFAULT_ADMIN_ROLE`, it only allows granting if there isn’t already a `default_admin` or if the role has been previously renounced. + +Exposing this function through another mechanism may make the `DEFAULT_ADMIN_ROLE` assignable again. Make sure to guarantee this is the expected behavior in your implementation. + +May emit a [RoleGranted](#AccessControlDefaultAdminRulesComponent-RoleGranted) event. + +#### [](#AccessControlDefaultAdminRulesComponent-_grant_role_with_delay)`_grant_role_with_delay(ref self: ContractState, role: felt252, account: ContractAddress, delay: u64)` external + +Attempts to grant `role` to `account` with the specified activation delay. + +The role will become effective after the given delay has passed. If the role is already active (`Effective`) for the account, the function will panic. If the role has been granted but is not yet active (being in the `Delayed` state), the existing delay will be overwritten with the new `delay`. + +Internal function without access restriction. + +Requirements: + +- `delay` must be greater than 0. +- the `role` must not be already effective for `account`. +- `role` must not be `DEFAULT_ADMIN_ROLE`. + +May emit a [RoleGrantedWithDelay](#AccessControlDefaultAdminRulesComponent-RoleGrantedWithDelay) event. + +#### [](#AccessControlDefaultAdminRulesComponent-_revoke_role)`_revoke_role(ref self: ContractState, role: felt252, account: ContractAddress)` external + +Attempts to revoke `role` from `account`. The function does nothing if `role` is not effective for `account`. If `role` has been revoked from `account`, but is still active due to a time delay, the delay is removed and `role` becomes inactive immediately. + +Internal function without access restriction. + +May emit a [RoleRevoked](#AccessControlDefaultAdminRulesComponent-RoleRevoked) event. + +#### [](#AccessControlDefaultAdminRulesComponent-set_pending_default_admin)`set_pending_default_admin(ref self: ContractState, new_admin: ContractAddress, new_schedule: u64)` external + +Setter of the tuple for pending admin and its schedule. + +May emit a DefaultAdminTransferCanceled event. + +#### [](#AccessControlDefaultAdminRulesComponent-set_pending_delay)`set_pending_delay(ref self: ContractState, new_delay: u64, new_schedule: u64)` external + +Setter of the tuple for pending delay and its schedule. + +May emit a DefaultAdminDelayChangeCanceled event. + +#### [](#AccessControlDefaultAdminRulesComponent-delay_change_wait)`delay_change_wait(self: @ContractState, new_delay: u64) → u64` external + +Returns the amount of seconds to wait after the `new_delay` will become the new `default_admin_delay`. + +The value returned guarantees that if the delay is reduced, it will go into effect after a wait that honors the previously set delay. + +See [default\_admin\_delay\_increase\_wait](#AccessControlDefaultAdminRulesComponent-default_admin_delay_increase_wait). + +#### [](#AccessControlDefaultAdminRulesComponent-Events)Events + +#### [](#AccessControlDefaultAdminRulesComponent-RoleAdminChanged)`RoleAdminChanged(role: felt252, previous_admin_role: felt252, new_admin_role: felt252)` event + +Emitted when `new_admin_role` is set as ``role’s admin role, replacing `previous_admin_role`` + +`DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite `RoleAdminChanged` not being emitted signaling this. + +#### [](#AccessControlDefaultAdminRulesComponent-RoleGranted)`RoleGranted(role: felt252, account: ContractAddress, sender: ContractAddress)` event + +Emitted when `account` is granted `role`. + +`sender` is the account that originated the contract call, an account with the admin role or the deployer address if `_grant_role` is called from the constructor. + +#### [](#AccessControlDefaultAdminRulesComponent-RoleRevoked)`RoleRevoked(role: felt252, account: ContractAddress, sender: ContractAddress)` event + +Emitted when `role` is revoked for `account`. + +`sender` is the account that originated the contract call: + +- If using `revoke_role`, it is the admin role bearer. +- If using `renounce_role`, it is the role bearer (i.e. `account`). + +#### [](#AccessControlDefaultAdminRulesComponent-RoleGrantedWithDelay)`RoleGrantedWithDelay(role: felt252, account: ContractAddress, sender: ContractAddress, delay: u64)` event + +Emitted when `account` is granted `role` with a delay. + +`sender` is the account that originated the contract call, an account with the admin role or the deployer address if `_grant_role_with_delay` is called from the constructor. + +#### [](#AccessControlDefaultAdminRulesComponent-DefaultAdminTransferScheduled)`DefaultAdminTransferScheduled(new_admin: ContractAddress, accept_schedule: u64)` event + +Emitted when a `default_admin` transfer is started. + +Sets `new_admin` as the next address to become the `default_admin` by calling [accept\_default\_admin\_transfer](#AccessControlDefaultAdminRulesComponent-accept_default_admin_transfer) only after `accept_schedule` passes. + +#### [](#AccessControlDefaultAdminRulesComponent-DefaultAdminTransferCanceled)`DefaultAdminTransferCanceled()` event + +Emitted when a [pending\_default\_admin](#AccessControlDefaultAdminRulesComponent-pending_default_admin) is reset if it was never accepted, regardless of its schedule. + +#### [](#AccessControlDefaultAdminRulesComponent-DefaultAdminDelayChangeScheduled)`DefaultAdminDelayChangeScheduled(new_delay: u64, effect_schedule: u64)` event + +Emitted when a [default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-default_admin_delay) change is started. + +Sets `new_delay` as the next delay to be applied between default admins transfers after `effect_schedule` has passed. + +Emitted when a [pending\_default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-pending_default_admin_delay) is reset if its schedule didn’t pass. diff --git a/content/cairo-contracts/api/account.mdx b/content/cairo-contracts/api/account.mdx new file mode 100644 index 00000000..b671b9b9 --- /dev/null +++ b/content/cairo-contracts/api/account.mdx @@ -0,0 +1,614 @@ +--- +title: Account +--- + +This crate provides components to implement account contracts that can be used for interacting with the network. + +## [](#interfaces)Interfaces + +Starting from version `3.x.x`, the interfaces are no longer part of the `openzeppelin_account` package. The references documented here are contained in the `openzeppelin_interfaces` package version `v2.1.0`. + +### [](#ISRC6)`ISRC6`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/interfaces/src/account/accounts.cairo) + +```cairo +use openzeppelin_interfaces::accounts::ISRC6; +``` + +Interface of the SRC6 Standard Account as defined in the [SNIP-6](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-6.md). + +[SRC5 ID](introspection#ISRC5) + +0x2ceccef7f994940b3962a6c67e0ba4fcd37df7d131417c604f91e03caecc1cd + +Functions + +- [`__execute__(calls)`](#ISRC6-__execute__) +- [`__validate__(calls)`](#ISRC6-__validate__) +- [`is_valid_signature(hash, signature)`](#ISRC6-is_valid_signature) + +#### [](#ISRC6-Functions)Functions + +#### [](#ISRC6-__execute__)`__execute__(calls: Array)` external + +Executes the list of calls as a transaction after validation. + +The `Call` struct is defined in [corelib](https://github.com/starkware-libs/cairo/blob/main/corelib/src/starknet/account.cairo#L3). + +#### [](#ISRC6-__validate__)`__validate__(calls: Array) → felt252` external + +Validates a transaction before execution. + +Returns the short string `'VALID'` if valid, otherwise it reverts. + +#### [](#ISRC6-is_valid_signature)`is_valid_signature(hash: felt252, signature: Array) → felt252` external + +Validates whether a signature is valid or not for the given message hash. + +Returns the short string `'VALID'` if valid, otherwise it reverts. + +### [](#ISRC9_V2)`ISRC9_V2`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/interfaces/src/account/src9.cairo) + +```cairo +use openzeppelin_interfaces::src9::ISRC9_V2; +``` + +Interface of the SRC9 Standard as defined in the [SNIP-9](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-9.md). + +[SRC5 ID](introspection#ISRC5) + +0x1d1144bb2138366ff28d8e9ab57456b1d332ac42196230c3a602003c89872 + +Functions + +- [`execute_from_outside_v2(outside_execution, signature)`](#ISRC9_V2-execute_from_outside_v2) +- [`is_valid_outside_execution_nonce(nonce)`](#ISRC9_V2-is_valid_outside_execution_nonce) + +#### [](#ISRC9_V2-Functions)Functions + +#### [](#ISRC9_V2-execute_from_outside_v2)`execute_from_outside_v2(outside_execution: OutsideExecution, signature: Span,) → Array>` external + +Allows anyone to submit a transaction on behalf of the account as long as they have the relevant signatures. + +This method allows reentrancy. A call to `__execute__` or `execute_from_outside_v2` can trigger another nested transaction to `execute_from_outside_v2` thus the implementation MUST verify that the provided `signature` matches the hash of `outside_execution` and that `nonce` was not already used. + +The implementation should expect version to be set to 2 in the domain separator. + +Arguments: + +- `outside_execution` - The parameters of the transaction to execute. +- `signature` - A valid signature on the [SNIP-12](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-12.md) message encoding of `outside_execution`. + +#### [](#ISRC9_V2-is_valid_outside_execution_nonce)`is_valid_outside_execution_nonce(nonce: felt252) → bool` external + +Get the status of a given nonce. `true` if the nonce is available to use. + +## [](#core)Core + +### [](#AccountComponent)`AccountComponent`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/account/src/account.cairo) + +```cairo +use openzeppelin_account::AccountComponent; +``` + +Account component implementing [`ISRC6`](#ISRC6) for signatures over the [Starknet curve](https://docs.starknet.io/architecture-and-concepts/cryptography/#stark-curve). + +Implementing [SRC5Component](introspection#SRC5Component) is a requirement for this component to be implemented. + +[Embeddable Mixin Implementations](../components#mixins) + +AccountMixinImpl + +- [`SRC6Impl`](#AccountComponent-Embeddable-Impls-SRC6Impl) +- [`DeclarerImpl`](#AccountComponent-Embeddable-Impls-DeclarerImpl) +- [`DeployableImpl`](#AccountComponent-Embeddable-Impls-DeployableImpl) +- [`PublicKeyImpl`](#AccountComponent-Embeddable-Impls-PublicKeyImpl) +- [`SRC6CamelOnlyImpl`](#AccountComponent-Embeddable-Impls-SRC6CamelOnlyImpl) +- [`PublicKeyCamelImpl`](#AccountComponent-Embeddable-Impls-PublicKeyCamelImpl) +- [`SRC5Impl`](introspection#SRC5Component-Embeddable-Impls) + +Embeddable Implementations + +SRC6Impl + +- [`__execute__(self, calls)`](#AccountComponent-__execute__) +- [`__validate__(self, calls)`](#AccountComponent-__validate__) +- [`is_valid_signature(self, hash, signature)`](#AccountComponent-is_valid_signature) + +DeclarerImpl + +- [`__validate_declare__(self, class_hash)`](#AccountComponent-__validate_declare__) + +DeployableImpl + +- [`__validate_deploy__(self, hash, signature)`](#AccountComponent-__validate_deploy__) + +PublicKeyImpl + +- [`get_public_key(self)`](#AccountComponent-get_public_key) +- [`set_public_key(self, new_public_key, signature)`](#AccountComponent-set_public_key) + +SRC6CamelOnlyImpl + +- [`isValidSignature(self, hash, signature)`](#AccountComponent-isValidSignature) + +PublicKeyCamelImpl + +- [`getPublicKey(self)`](#AccountComponent-getPublicKey) +- [`setPublicKey(self, newPublicKey, signature)`](#AccountComponent-setPublicKey) + +SRC5Impl + +- [`supports_interface(self, interface_id: felt252)`](introspection#ISRC5-supports_interface) + +Internal Implementations + +InternalImpl + +- [`initializer(self, public_key)`](#AccountComponent-initializer) +- [`assert_only_self(self)`](#AccountComponent-assert_only_self) +- [`assert_valid_new_owner(self, current_owner, new_owner, signature)`](#AccountComponent-assert_valid_new_owner) +- [`validate_transaction(self)`](#AccountComponent-validate_transaction) +- [`_set_public_key(self, new_public_key)`](#AccountComponent-_set_public_key) +- [`_is_valid_signature(self, hash, signature)`](#AccountComponent-_is_valid_signature) + +Events + +- [`OwnerAdded(new_owner_guid)`](#AccountComponent-OwnerAdded) +- [`OwnerRemoved(removed_owner_guid)`](#AccountComponent-OwnerRemoved) + +#### [](#AccountComponent-Embeddable-Functions)Embeddable functions + +#### [](#AccountComponent-__execute__)`__execute__(self: @ContractState, calls: Array)` external + +See [ISRC6::\_\_execute\_\_](#ISRC6-__execute__). + +#### [](#AccountComponent-__validate__)`__validate__(self: @ContractState, calls: Array) → felt252` external + +See [ISRC6::\_\_validate\_\_](#ISRC6-__validate__). + +#### [](#AccountComponent-is_valid_signature)`is_valid_signature(self: @ContractState, hash: felt252, signature: Array) → felt252` external + +See [ISRC6::is\_valid\_signature](#ISRC6-is_valid_signature). + +#### [](#AccountComponent-__validate_declare__)`__validate_declare__(self: @ContractState, class_hash: felt252) → felt252` external + +Validates a [`Declare` transaction](https://docs.starknet.io/architecture-and-concepts/network-architecture/transactions/#declare-transaction). + +Returns the short string `'VALID'` if valid, otherwise it reverts. + +#### [](#AccountComponent-__validate_deploy__)`__validate_deploy__(self: @ContractState, class_hash: felt252, contract_address_salt: felt252, public_key: felt252) → felt252` external + +Validates a [`DeployAccount` transaction](https://docs.starknet.io/architecture-and-concepts/network-architecture/transactions/#deploy_account_transaction). See [Counterfactual Deployments](../guides/deployment). + +Returns the short string `'VALID'` if valid, otherwise it reverts. + +#### [](#AccountComponent-get_public_key)`get_public_key(self: @ContractState) → felt252` external + +Returns the current public key of the account. + +#### [](#AccountComponent-set_public_key)`set_public_key(ref self: ContractState, new_public_key: felt252, signature: Span)` external + +Sets a new public key for the account. Only accessible by the account calling itself through `__execute__`. + +Requirements: + +- The caller must be the contract itself. +- The signature must be valid for the new owner. + +Emits both an [OwnerRemoved](#AccountComponent-OwnerRemoved) and an [OwnerAdded](#AccountComponent-OwnerAdded) event. + +The message to be signed is computed in Cairo as follows: + +```javascript +let message_hash = PoseidonTrait::new() + .update_with('StarkNet Message') + .update_with('accept_ownership') + .update_with(get_contract_address()) + .update_with(current_owner) + .finalize(); +``` + +#### [](#AccountComponent-isValidSignature)`isValidSignature(self: @ContractState, hash: felt252, signature: Array) → felt252` external + +See [ISRC6::is\_valid\_signature](#ISRC6-is_valid_signature). + +#### [](#AccountComponent-getPublicKey)`getPublicKey(self: @ContractState) → felt252` external + +See [get\_public\_key](#AccountComponent-get_public_key). + +#### [](#AccountComponent-setPublicKey)`setPublicKey(ref self: ContractState, newPublicKey: felt252, signature: Span)` external + +See [set\_public\_key](#AccountComponent-set_public_key). + +#### [](#AccountComponent-Internal-Functions)Internal functions + +#### [](#AccountComponent-initializer)`initializer(ref self: ComponentState, public_key: felt252)` internal + +Initializes the account with the given public key, and registers the `ISRC6` interface ID. + +Emits an [OwnerAdded](#AccountComponent-OwnerAdded) event. + +#### [](#AccountComponent-assert_only_self)`assert_only_self(self: @ComponentState)` internal + +Validates that the caller is the account itself. Otherwise it reverts. + +#### [](#AccountComponent-assert_valid_new_owner)`assert_valid_new_owner(self: @ComponentState, current_owner: felt252, new_owner: felt252, signature: Span)` internal + +Validates that `new_owner` accepted the ownership of the contract through a signature. + +Requirements: + +- `signature` must be valid for the new owner. + +This function assumes that `current_owner` is the current owner of the contract, and does not validate this assumption. + +#### [](#AccountComponent-validate_transaction)`validate_transaction(self: @ComponentState) → felt252` internal + +Validates a transaction signature from the [global context](https://github.com/starkware-libs/cairo/blob/main/corelib/src/starknet/info.cairo#L61). + +Returns the short string `'VALID'` if valid, otherwise it reverts. + +#### [](#AccountComponent-_set_public_key)`_set_public_key(ref self: ComponentState, new_public_key: felt252)` internal + +Set the public key without validating the caller. + +Emits an [OwnerAdded](#AccountComponent-OwnerAdded) event. + +The usage of this method outside the `set_public_key` function is discouraged. + +#### [](#AccountComponent-_is_valid_signature)`_is_valid_signature(self: @ComponentState, hash: felt252, signature: Span) → bool` internal + +Validates the provided `signature` for the `hash`, using the account’s current public key. + +#### [](#AccountComponent-Events)Events + +#### [](#AccountComponent-OwnerAdded)`OwnerAdded(new_owner_guid: felt252)` event + +Emitted when a `public_key` is added. + +#### [](#AccountComponent-OwnerRemoved)`OwnerRemoved(removed_owner_guid: felt252)` event + +Emitted when a `public_key` is removed. + +### [](#EthAccountComponent)`EthAccountComponent`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/account/src/eth_account.cairo) + +```cairo +use openzeppelin_account::eth_account::EthAccountComponent; +``` + +Account component implementing [`ISRC6`](#ISRC6) for signatures over the [Secp256k1 curve](https://en.bitcoin.it/wiki/Secp256k1). + +Implementing [SRC5Component](introspection#SRC5Component) is a requirement for this component to be implemented. + +The `EthPublicKey` type is an alias for `starknet::secp256k1::Secp256k1Point`. + +[Embeddable Mixin Implementations](../components#mixins) + +EthAccountMixinImpl + +- [`SRC6Impl`](#EthAccountComponent-Embeddable-Impls-SRC6Impl) +- [`DeclarerImpl`](#EthAccountComponent-Embeddable-Impls-DeclarerImpl) +- [`DeployableImpl`](#EthAccountComponent-Embeddable-Impls-DeployableImpl) +- [`PublicKeyImpl`](#EthAccountComponent-Embeddable-Impls-PublicKeyImpl) +- [`SRC6CamelOnlyImpl`](#EthAccountComponent-Embeddable-Impls-SRC6CamelOnlyImpl) +- [`PublicKeyCamelImpl`](#EthAccountComponent-Embeddable-Impls-PublicKeyCamelImpl) +- [`SRC5Impl`](introspection#SRC5Component-Embeddable-Impls) + +Embeddable Implementations + +SRC6Impl + +- [`__execute__(self, calls)`](#EthAccountComponent-__execute__) +- [`__validate__(self, calls)`](#EthAccountComponent-__validate__) +- [`is_valid_signature(self, hash, signature)`](#EthAccountComponent-is_valid_signature) + +DeclarerImpl + +- [`__validate_declare__(self, class_hash)`](#EthAccountComponent-__validate_declare__) + +DeployableImpl + +- [`__validate_deploy__(self, hash, signature)`](#EthAccountComponent-__validate_deploy__) + +PublicKeyImpl + +- [`get_public_key(self)`](#EthAccountComponent-get_public_key) +- [`set_public_key(self, new_public_key, signature)`](#EthAccountComponent-set_public_key) + +SRC6CamelOnlyImpl + +- [`isValidSignature(self, hash, signature)`](#EthAccountComponent-isValidSignature) + +PublicKeyCamelImpl + +- [`getPublicKey(self)`](#EthAccountComponent-getPublicKey) +- [`setPublicKey(self, newPublicKey, signature)`](#EthAccountComponent-setPublicKey) + +SRC5Impl + +- [`supports_interface(self, interface_id: felt252)`](introspection#ISRC5-supports_interface) + +Internal Implementations + +InternalImpl + +- [`initializer(self, public_key)`](#EthAccountComponent-initializer) +- [`assert_only_self(self)`](#EthAccountComponent-assert_only_self) +- [`assert_valid_new_owner(self, current_owner, new_owner, signature)`](#EthAccountComponent-assert_valid_new_owner) +- [`validate_transaction(self)`](#EthAccountComponent-validate_transaction) +- [`_set_public_key(self, new_public_key)`](#EthAccountComponent-_set_public_key) +- [`_is_valid_signature(self, hash, signature)`](#EthAccountComponent-_is_valid_signature) + +Events + +- [`OwnerAdded(new_owner_guid)`](#EthAccountComponent-OwnerAdded) +- [`OwnerRemoved(removed_owner_guid)`](#EthAccountComponent-OwnerRemoved) + +#### [](#EthAccountComponent-Embeddable-Functions)Embeddable functions + +#### [](#EthAccountComponent-__execute__)`__execute__(self: @ContractState, calls: Array)` external + +See [ISRC6::\_\_execute\_\_](#ISRC6-__execute__). + +#### [](#EthAccountComponent-__validate__)`__validate__(self: @ContractState, calls: Array) → felt252` external + +See [ISRC6::\_\_validate\_\_](#ISRC6-__validate__). + +#### [](#EthAccountComponent-is_valid_signature)`is_valid_signature(self: @ContractState, hash: felt252, signature: Array) → felt252` external + +See [ISRC6::is\_valid\_signature](#ISRC6-is_valid_signature). + +#### [](#EthAccountComponent-__validate_declare__)`__validate_declare__(self: @ContractState, class_hash: felt252) → felt252` external + +Validates a [`Declare` transaction](https://docs.starknet.io/architecture-and-concepts/network-architecture/transactions/#declare-transaction). + +Returns the short string `'VALID'` if valid, otherwise it reverts. + +#### [](#EthAccountComponent-__validate_deploy__)`__validate_deploy__(self: @ContractState, class_hash: felt252, contract_address_salt: felt252, public_key: EthPublicKey) → felt252` external + +Validates a [`DeployAccount` transaction](https://docs.starknet.io/architecture-and-concepts/network-architecture/transactions/#deploy_account_transaction). See [Counterfactual Deployments](../guides/deployment). + +Returns the short string `'VALID'` if valid, otherwise it reverts. + +#### [](#EthAccountComponent-get_public_key)`get_public_key(self: @ContractState) → EthPublicKey` external + +Returns the current public key of the account. + +#### [](#EthAccountComponent-set_public_key)`set_public_key(ref self: ContractState, new_public_key: EthPublicKey, signature: Span)` external + +Sets a new public key for the account. Only accessible by the account calling itself through `__execute__`. + +Requirements: + +- The caller must be the contract itself. +- The signature must be valid for the new owner. + +Emits both an [OwnerRemoved](#EthAccountComponent-OwnerRemoved) and an [OwnerAdded](#EthAccountComponent-OwnerAdded) event. + +The message to be signed is computed in Cairo as follows: + +```javascript +let message_hash = PoseidonTrait::new() + .update_with('StarkNet Message') + .update_with('accept_ownership') + .update_with(get_contract_address()) + .update_with(current_owner.get_coordinates().unwrap_syscall()) + .finalize(); +``` + +#### [](#EthAccountComponent-isValidSignature)`isValidSignature(self: @ContractState, hash: felt252, signature: Array) → felt252` external + +See [ISRC6::is\_valid\_signature](#ISRC6-is_valid_signature). + +#### [](#EthAccountComponent-getPublicKey)`getPublicKey(self: @ContractState) → EthPublicKey` external + +See [get\_public\_key](#EthAccountComponent-get_public_key). + +#### [](#EthAccountComponent-setPublicKey)`setPublicKey(ref self: ContractState, newPublicKey: EthPublicKey, signature: Span)` external + +See [set\_public\_key](#EthAccountComponent-set_public_key). + +#### [](#EthAccountComponent-Internal-Functions)Internal functions + +#### [](#EthAccountComponent-initializer)`initializer(ref self: ComponentState, public_key: EthPublicKey)` internal + +Initializes the account with the given public key, and registers the `ISRC6` interface ID. + +Emits an [OwnerAdded](#EthAccountComponent-OwnerAdded) event. + +#### [](#EthAccountComponent-assert_only_self)`assert_only_self(self: @ComponentState)` internal + +Validates that the caller is the account itself. Otherwise it reverts. + +#### [](#EthAccountComponent-assert_valid_new_owner)`assert_valid_new_owner(self: @ComponentState, current_owner: EthPublicKey, new_owner: EthPublicKey, signature: Span)` internal + +Validates that `new_owner` accepted the ownership of the contract through a signature. + +Requirements: + +- The signature must be valid for the `new_owner`. + +This function assumes that `current_owner` is the current owner of the contract, and does not validate this assumption. + +#### [](#EthAccountComponent-validate_transaction)`validate_transaction(self: @ComponentState) → felt252` internal + +Validates a transaction signature from the [global context](https://github.com/starkware-libs/cairo/blob/main/corelib/src/starknet/info.cairo#L61). + +Returns the short string `'VALID'` if valid, otherwise it reverts. + +#### [](#EthAccountComponent-_set_public_key)`_set_public_key(ref self: ComponentState, new_public_key: EthPublicKey)` internal + +Set the public key without validating the caller. + +Emits an [OwnerAdded](#EthAccountComponent-OwnerAdded) event. + +The usage of this method outside the `set_public_key` function is discouraged. + +#### [](#EthAccountComponent-_is_valid_signature)`_is_valid_signature(self: @ComponentState, hash: felt252, signature: Span) → bool` internal + +Validates the provided `signature` for the `hash`, using the account’s current public key. + +#### [](#EthAccountComponent-Events)Events + +The `guid` is computed as the hash of the public key, using the poseidon hash function. + +#### [](#EthAccountComponent-OwnerAdded)`OwnerAdded(new_owner_guid: felt252)` event + +Emitted when a `public_key` is added. + +#### [](#EthAccountComponent-OwnerRemoved)`OwnerRemoved(removed_owner_guid: felt252)` event + +Emitted when a `public_key` is removed. + +## [](#extensions)Extensions + +### [](#SRC9Component)`SRC9Component`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/account/src/extensions/src9/src9.cairo) + +```cairo +use openzeppelin_account::extensions::SRC9Component; +``` + +OutsideExecution component implementing [`ISRC9_V2`](#ISRC9_V2). + +This component is signature-agnostic, meaning it can be integrated into any account contract, as long as the account implements the ISRC6 interface. + +Embeddable Implementations + +OutsideExecutionV2Impl + +- [`execute_from_outside_v2(self, outside_execution, signature)`](#SRC9Component-execute_from_outside_v2) +- [`is_valid_outside_execution_nonce(self, nonce)`](#SRC9Component-is_valid_outside_execution_nonce) + +Internal Implementations + +InternalImpl + +- [`initializer(self)`](#SRC9Component-initializer) + +#### [](#SRC9Component-Embeddable-Functions)Embeddable functions + +#### [](#SRC9Component-execute_from_outside_v2)`execute_from_outside_v2(ref self: ContractState, outside_execution: OutsideExecution, signature: Span) → Array>` external + +Allows anyone to submit a transaction on behalf of the account as long as they have the relevant signatures. + +This method allows reentrancy. A call to `__execute__` or `execute_from_outside_v2` can trigger another nested transaction to `execute_from_outside_v2`. This implementation verifies that the provided `signature` matches the hash of `outside_execution` and that `nonce` was not already used. + +Arguments: + +- `outside_execution` - The parameters of the transaction to execute. +- `signature` - A valid signature on the [SNIP-12](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-12.md) message encoding of `outside_execution`. + +Requirements: + +- The caller must be the `outside_execution.caller` unless 'ANY\_CALLER' is used. +- The current time must be within the `outside_execution.execute_after` and `outside_execution.execute_before` span. +- The `outside_execution.nonce` must not be used before. +- The `signature` must be valid. + +#### [](#SRC9Component-is_valid_outside_execution_nonce)`is_valid_outside_execution_nonce(self: @ContractState, nonce: felt252) → bool` external + +Returns the status of a given nonce. `true` if the nonce is available to use. + +#### [](#SRC9Component-Internal-Functions)Internal functions + +#### [](#SRC9Component-initializer)`initializer(ref self: ComponentState)` internal + +Initializes the account by registering the `ISRC9_V2` interface ID. + +## [](#presets)Presets + +### [](#AccountUpgradeable)`AccountUpgradeable`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/presets/src/account.cairo) + +```cairo +use openzeppelin_presets::AccountUpgradeable; +``` + +Upgradeable account which can change its public key and declare, deploy, or call contracts. Supports outside execution by implementing [SRC9](#SRC9Component). + +[Sierra class hash](../presets) + +0x002de258cce5b9e160bf83956b09f982059582469f7e6fad07b438128317d029 + +Constructor + +- [`constructor(self, public_key)`](#AccountUpgradeable-constructor) + +Embedded Implementations + +AccountComponent + +- [`AccountMixinImpl`](#AccountComponent-Embeddable-Mixin-Impl) + +SRC9Component + +- [`OutsideExecutionV2Impl`](#SRC9Component-Embeddable-Impls-OutsideExecutionV2Impl) + +External Functions + +- [`upgrade(self, new_class_hash)`](#AccountUpgradeable-upgrade) + +#### [](#AccountUpgradeable-constructor-section)Constructor + +#### [](#AccountUpgradeable-constructor)`constructor(ref self: ContractState, public_key: felt252)` constructor + +Sets the account `public_key` and registers the interfaces the contract supports. + +#### [](#AccountUpgradeable-external-functions)External functions + +#### [](#AccountUpgradeable-upgrade)`upgrade(ref self: ContractState, new_class_hash: ClassHash)` external + +Upgrades the contract to a new implementation given by `new_class_hash`. + +Requirements: + +- The caller is the account contract itself. +- `new_class_hash` cannot be zero. + +### [](#EthAccountUpgradeable)`EthAccountUpgradeable`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/presets/src/eth_account.cairo) + +```cairo +use openzeppelin_presets::EthAccountUpgradeable; +``` + +Upgradeable account which can change its public key and declare, deploy, or call contracts, using Ethereum signing keys. Supports outside execution by implementing [SRC9](#SRC9Component). + +The `EthPublicKey` type is an alias for `starknet::secp256k1::Secp256k1Point`. + +[Sierra class hash](../presets) + +0x07f54a43da3f7beb5099c87f5627b7fba5f31c7a704cce57f8fb73287c1ea3be + +Constructor + +- [`constructor(self, public_key)`](#EthAccountUpgradeable-constructor) + +Embedded Implementations + +EthAccountComponent + +- [`EthAccountMixinImpl`](#EthAccountComponent-Embeddable-Mixin-Impl) + +SRC9Component + +- [`OutsideExecutionV2Impl`](#SRC9Component-Embeddable-Impls-OutsideExecutionV2Impl) + +External Functions + +- [`upgrade(self, new_class_hash)`](#EthAccountUpgradeable-upgrade) + +#### [](#EthAccountUpgradeable-constructor-section)Constructor + +#### [](#EthAccountUpgradeable-constructor)`constructor(ref self: ContractState, public_key: EthPublicKey)` constructor + +Sets the account `public_key` and registers the interfaces the contract supports. + +#### [](#EthAccountUpgradeable-external-functions)External functions + +#### [](#EthAccountUpgradeable-upgrade)`upgrade(ref self: ContractState, new_class_hash: ClassHash)` external + +Upgrades the contract to a new implementation given by `new_class_hash`. + +Requirements: + +- The caller is the account contract itself. +- `new_class_hash` cannot be zero. diff --git a/content/cairo-contracts/api/erc1155.mdx b/content/cairo-contracts/api/erc1155.mdx new file mode 100644 index 00000000..4617054a --- /dev/null +++ b/content/cairo-contracts/api/erc1155.mdx @@ -0,0 +1,553 @@ +--- +title: ERC1155 +--- + +This module provides interfaces, presets, and utilities related to ERC1155 contracts. + +For an overview of ERC1155, read our [ERC1155 guide](../erc1155). + +## [](#interfaces)Interfaces + +Starting from version `3.x.x`, the interfaces are no longer part of the `openzeppelin_token` package. The references documented here are contained in the `openzeppelin_interfaces` package version `v2.1.0`. + +### [](#IERC1155)`IERC1155`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/interfaces/src/token/erc1155.cairo) + +```cairo +use openzeppelin_interfaces::erc1155::IERC1155; +``` + +Interface of the IERC1155 standard as defined in [EIP1155](https://eips.ethereum.org/EIPS/eip-1155). + +[SRC5 ID](introspection#ISRC5) + +0x6114a8f75559e1b39fcba08ce02961a1aa082d9256a158dd3e64964e4b1b52 + +Functions + +- [`balance_of(account, token_id)`](#IERC1155-balance_of) +- [`balance_of_batch(accounts, token_ids)`](#IERC1155-balance_of_batch) +- [`safe_transfer_from(from, to, token_id, value, data)`](#IERC1155-safe_transfer_from) +- [`safe_batch_transfer_from(from, to, token_ids, values, data)`](#IERC1155-safe_batch_transfer_from) +- [`set_approval_for_all(operator, approved)`](#IERC1155-set_approval_for_all) +- [`is_approved_for_all(owner, operator)`](#IERC1155-is_approved_for_all) + +Events + +- [`TransferSingle(operator, from, to, id, value)`](#IERC1155-TransferSingle) +- [`TransferBatch(operator, from, to, ids, values)`](#IERC1155-TransferBatch) +- [`ApprovalForAll(owner, operator, approved)`](#IERC1155-ApprovalForAll) +- [`URI(value, id)`](#IERC1155-URI) + +#### [](#functions)Functions + +#### [](#IERC1155-balance_of)`balance_of(account: ContractAddress, token_id: u256) → u256` external + +Returns the amount of `token_id` tokens owned by `account`. + +#### [](#IERC1155-balance_of_batch)`balance_of_batch(accounts: Span, token_ids: Span) → Span` external + +Returns a list of balances derived from the `accounts` and `token_ids` pairs. + +#### [](#IERC1155-safe_transfer_from)`safe_transfer_from(from: ContractAddress, to: ContractAddress, token_id: u256, value: u256, data: Span)` external + +Transfers ownership of `value` amount of `token_id` from `from` if `to` is either `IERC1155Receiver` or an account. + +`data` is additional data, it has no specified format and it is passed to `to`. + +Emits a [TransferSingle](#IERC1155-TransferSingle) event. + +#### [](#IERC1155-safe_batch_transfer_from)`safe_batch_transfer_from(from: ContractAddress, to: ContractAddress, token_ids: Span, values: Span, data: Span)` external + +Transfers ownership of `token_ids` and `values` pairs from `from` if `to` is either `IERC1155Receiver` or an account. + +`data` is additional data, it has no specified format and it is passed to `to`. + +Emits a [TransferBatch](#IERC1155-TransferBatch) event. + +#### [](#IERC1155-set_approval_for_all)`set_approval_for_all(operator: ContractAddress, approved: bool)` external + +Enables or disables approval for `operator` to manage all of the caller’s assets. + +Emits an [ApprovalForAll](#IERC1155-ApprovalForAll) event. + +#### [](#IERC1155-is_approved_for_all)`is_approved_for_all(owner: ContractAddress, operator: ContractAddress) -> bool` external + +Queries if `operator` is an authorized operator for `owner`. + +#### [](#events)Events + +#### [](#IERC1155-TransferSingle)`TransferSingle(operator: ContractAddress, from: ContractAddress, to: ContractAddress, id: u256, value: u256)` event + +Emitted when `value` amount of `id` token is transferred from `from` to `to` through `operator`. + +#### [](#IERC1155-TransferBatch)`TransferBatch(operator: ContractAddress, from: ContractAddress, to: ContractAddress, ids: Span, values: Span)` event + +Emitted when a batch of `values` amount of `ids` tokens are transferred from `from` to `to` through `operator`. + +#### [](#IERC1155-ApprovalForAll)`ApprovalForAll(owner: ContractAddress, operator: ContractAddress, approved: bool)` event + +Emitted when `owner` enables or disables `operator` to manage all of the owner’s assets. + +#### [](#IERC1155-URI)`URI(value: ByteArray, id: u256)` event + +Emitted when the token URI is updated to `value` for the `id` token. + +### [](#IERC1155MetadataURI)`IERC1155MetadataURI`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/interfaces/src/token/erc1155.cairo) + +```cairo +use openzeppelin_interfaces::erc1155::IERC1155MetadataURI; +``` + +Interface for the optional metadata function in [EIP1155](https://eips.ethereum.org/EIPS/eip-1155#metadata). + +[SRC5 ID](introspection#ISRC5) + +0xcabe2400d5fe509e1735ba9bad205ba5f3ca6e062da406f72f113feb889ef7 + +Functions + +- [`uri(token_id)`](#IERC1155MetadataURI-uri) + +#### [](#functions_2)Functions + +#### [](#IERC1155MetadataURI-uri)`uri(token_id: u256) -> ByteArray` external + +Returns the Uniform Resource Identifier (URI) for the `token_id` token. + +### [](#IERC1155Receiver)`IERC1155Receiver`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/interfaces/src/token/erc1155.cairo) + +```cairo +use openzeppelin_interfaces::erc1155::IERC1155Receiver; +``` + +Interface for contracts that support receiving token transfers from `ERC1155` contracts. + +[SRC5 ID](introspection#ISRC5) + +0x15e8665b5af20040c3af1670509df02eb916375cdf7d8cbaf7bd553a257515e + +Functions + +- [`on_erc1155_received(operator, from, token_id, value, data)`](#IERC1155Receiver-on_erc1155_received) +- [`on_erc1155_batch_received(operator, from, token_ids, values, data)`](#IERC1155Receiver-on_erc1155_batch_received) + +#### [](#functions_3)Functions + +#### [](#IERC1155Receiver-on_erc1155_received)`on_erc1155_received(operator: ContractAddress, from: ContractAddress, token_id: u256, value: u256, data Span) -> felt252` external + +This function is called whenever an ERC1155 `token_id` token is transferred to this `IERC1155Receiver` implementer via [IERC1155::safe\_transfer\_from](#IERC1155-safe_transfer_from) by `operator` from `from`. + +#### [](#IERC1155Receiver-on_erc1155_batch_received)`on_erc1155_batch_received(operator: ContractAddress, from: ContractAddress, token_ids: Span, values: Span, data Span) -> felt252` external + +This function is called whenever multiple ERC1155 `token_ids` tokens are transferred to this `IERC1155Receiver` implementer via [IERC1155::safe\_batch\_transfer\_from](#IERC1155-safe_batch_transfer_from) by `operator` from `from`. + +## [](#core)Core + +### [](#ERC1155Component)`ERC1155Component`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/token/src/erc1155/erc1155.cairo) + +```cairo +use openzeppelin_token::erc1155::ERC1155Component; +``` + +ERC1155 component implementing [IERC1155](#IERC1155) and [IERC1155MetadataURI](#IERC1155MetadataURI). + +Implementing [SRC5Component](introspection#SRC5Component) is a requirement for this component to be implemented. + +See [Hooks](#ERC1155Component-Hooks) to understand how are hooks used. + +Hooks + +ERC1155HooksTrait + +- [`before_update(self, from, to, token_ids, values)`](#ERC1155Component-before_update) +- [`after_update(self, from, to, token_ids, values)`](#ERC1155Component-after_update) + +[Embeddable Mixin Implementations](../components#mixins) + +ERC1155MixinImpl + +- [`ERC1155Impl`](#ERC1155Component-Embeddable-Impls-ERC1155Impl) +- [`ERC1155MetadataURIImpl`](#ERC1155Component-Embeddable-Impls-ERC1155MetadataURIImpl) +- [`ERC1155CamelImpl`](#ERC1155Component-Embeddable-Impls-ERC1155CamelImpl) +- [`SRC5Impl`](introspection#SRC5Component-Embeddable-Impls-SRC5Impl) + +Embeddable Implementations + +ERC1155Impl + +- [`balance_of(self, account, token_id)`](#ERC1155Component-balance_of) +- [`balance_of_batch(self, accounts, token_ids)`](#ERC1155Component-balance_of_batch) +- [`safe_transfer_from(self, from, to, token_id, value, data)`](#ERC1155Component-safe_transfer_from) +- [`safe_batch_transfer_from(self, from, to, token_ids, values, data)`](#ERC1155Component-safe_batch_transfer_from) +- [`set_approval_for_all(self, operator, approved)`](#ERC1155Component-set_approval_for_all) +- [`is_approved_for_all(self, owner, operator)`](#ERC1155Component-is_approved_for_all) + +ERC1155MetadataURIImpl + +- [`uri(self, token_id)`](#ERC1155Component-uri) + +ERC1155CamelImpl + +- [`balanceOf(self, account, tokenId)`](#ERC1155Component-balanceOf) +- [`balanceOfBatch(self, accounts, tokenIds)`](#ERC1155Component-balanceOfBatch) +- [`safeTransferFrom(self, from, to, tokenId, value, data)`](#ERC1155Component-safeTransferFrom) +- [`safeBatchTransferFrom(self, from, to, tokenIds, values, data)`](#ERC1155Component-safeBatchTransferFrom) +- [`setApprovalForAll(self, operator, approved)`](#ERC1155Component-setApprovalForAll) +- [`isApprovedForAll(self, owner, operator)`](#ERC1155Component-isApprovedForAll) + +Internal Functions + +InternalImpl + +- [`initializer(self, base_uri)`](#ERC1155Component-initializer) +- [`initializer_no_metadata(self)`](#ERC1155Component-initializer_no_metadata) +- [`mint_with_acceptance_check(self, to, token_id, value, data)`](#ERC1155Component-mint_with_acceptance_check) +- [`batch_mint_with_acceptance_check(self, to, token_ids, values, data)`](#ERC1155Component-batch_mint_with_acceptance_check) +- [`burn(self, from, token_id, value)`](#ERC1155Component-burn) +- [`batch_burn(self, from, token_ids, values)`](#ERC1155Component-batch_burn) +- [`update_with_acceptance_check(self, from, to, token_ids, values, data)`](#ERC1155Component-update_with_acceptance_check) +- [`update(self, from, to, token_ids, values)`](#ERC1155Component-update) +- [`_set_base_uri(self, base_uri)`](#ERC1155Component-_set_base_uri) + +Events + +IERC1155 + +- [`TransferSingle(operator, from, to, id, value)`](#ERC1155Component-TransferSingle) +- [`TransferBatch(operator, from, to, ids, values)`](#ERC1155Component-TransferBatch) +- [`ApprovalForAll(owner, operator, approved)`](#ERC1155Component-ApprovalForAll) +- [`URI(value, id)`](#ERC1155Component-URI) + +#### [](#ERC1155Component-Hooks)Hooks + +Hooks are functions which implementations can extend the functionality of the component source code. Every contract using ERC1155Component is expected to provide an implementation of the ERC1155HooksTrait. For basic token contracts, an empty implementation with no logic must be provided. + +You can use `openzeppelin_token::erc1155::ERC1155HooksEmptyImpl` which is already available as part of the library for this purpose. + +#### [](#ERC1155Component-before_update)`before_update(ref self: ContractState, from: ContractAddress, to: ContractAddress, token_ids: Span, values: Span)` hook + +Function executed at the beginning of the [update](#ERC1155Component-update) function prior to any other logic. + +#### [](#ERC1155Component-after_update)`after_update(ref self: ContractState, from: ContractAddress, to: ContractAddress, token_ids: Span, values: Span)` hook + +Function executed at the end of the [update](#ERC1155Component-update) function. + +#### [](#embeddable_functions)Embeddable functions + +#### [](#ERC1155Component-balance_of)`balance_of(self: @ContractState, account: ContractAddress, token_id: u256) → u256` external + +Returns the amount of `token_id` tokens owned by `account`. + +#### [](#ERC1155Component-balance_of_batch)`balance_of_batch(self: @ContractState, accounts: Span, token_ids: Span) → Span` external + +Returns a list of balances derived from the `accounts` and `token_ids` pairs. + +Requirements: + +- `token_ids` and `accounts` must have the same length. + +#### [](#ERC1155Component-safe_transfer_from)`safe_transfer_from(ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256, value: u256, data: Span)` external + +Transfers ownership of `value` amount of `token_id` from `from` if `to` is either an account or `IERC1155Receiver`. + +`data` is additional data, it has no specified format and it is passed to `to`. + +This function can potentially allow a reentrancy attack when transferring tokens to an untrusted contract, when invoking `on_ERC1155_received` on the receiver. Ensure to follow the checks-effects-interactions pattern and consider employing reentrancy guards when interacting with untrusted contracts. + +Requirements: + +- Caller is either approved or the `token_id` owner. +- `from` is not the zero address. +- `to` is not the zero address. +- If `to` refers to a non-account contract, it must implement `IERC1155Receiver::on_ERC1155_received` and return the required magic value. + +Emits a [TransferSingle](#ERC1155Component-TransferSingle) event. + +#### [](#ERC1155Component-safe_batch_transfer_from)`safe_batch_transfer_from(ref self: ContractState, from: ContractAddress, to: ContractAddress, token_ids: Span, values: Span, data: Span)` external + +Transfers ownership of `values` and `token_ids` pairs from `from` if `to` is either an account or `IERC1155Receiver`. + +`data` is additional data, it has no specified format and it is passed to `to`. + +This function can potentially allow a reentrancy attack when transferring tokens to an untrusted contract, when invoking `on_ERC1155_batch_received` on the receiver. Ensure to follow the checks-effects-interactions pattern and consider employing reentrancy guards when interacting with untrusted contracts. + +Requirements: + +- Caller is either approved or the `token_id` owner. +- `from` is not the zero address. +- `to` is not the zero address. +- `token_ids` and `values` must have the same length. +- If `to` refers to a non-account contract, it must implement `IERC1155Receiver::on_ERC1155_batch_received` and return the acceptance magic value. + +Emits a [TransferSingle](#ERC1155Component-TransferSingle) event if the arrays contain one element, and [TransferBatch](#ERC1155Component-TransferBatch) otherwise. + +#### [](#ERC1155Component-set_approval_for_all)`set_approval_for_all(ref self: ContractState, operator: ContractAddress, approved: bool)` external + +Enables or disables approval for `operator` to manage all of the callers assets. + +Requirements: + +- `operator` cannot be the caller. + +Emits an [ApprovalForAll](#ERC1155Component-ApprovalForAll) event. + +#### [](#ERC1155Component-is_approved_for_all)`is_approved_for_all(self: @ContractState, owner: ContractAddress, operator: ContractAddress) -> bool` external + +Queries if `operator` is an authorized operator for `owner`. + +#### [](#ERC1155Component-uri)`uri(self: @ContractState, token_id: u256) -> ByteArray` external + +This implementation returns the same URI for **all** token types. It relies on the token type ID substitution mechanism [specified in the EIP](https://eips.ethereum.org/EIPS/eip-1155#metadata). + +Clients calling this function must replace the `id` substring with the actual token type ID. + +#### [](#ERC1155Component-balanceOf)`balanceOf(self: @ContractState, account: ContractAddress, tokenId: u256) → u256` external + +See [ERC1155Component::balance\_of](#ERC1155Component-balance_of). + +#### [](#ERC1155Component-balanceOfBatch)`balanceOfBatch(self: @ContractState, accounts: Span, tokenIds: Span) → Span` external + +See [ERC1155Component::balance\_of\_batch](#ERC1155Component-balance_of_batch). + +#### [](#ERC1155Component-safeTransferFrom)`safeTransferFrom(ref self: ContractState, from: ContractAddress, to: ContractAddress, tokenId: u256, value: u256, data: Span)` external + +See [ERC1155Component::safe\_transfer\_from](#ERC1155Component-safe_transfer_from). + +#### [](#ERC1155Component-safeBatchTransferFrom)`safeBatchTransferFrom(ref self: ContractState, from: ContractAddress, to: ContractAddress, tokenIds: Span, values: Span, data: Span)` external + +See [ERC1155Component::safe\_batch\_transfer\_from](#ERC1155Component-safe_batch_transfer_from). + +#### [](#ERC1155Component-setApprovalForAll)`setApprovalForAll(ref self: ContractState, operator: ContractAddress, approved: bool)` external + +See [ERC1155Component::set\_approval\_for\_all](#ERC1155Component-set_approval_for_all). + +#### [](#ERC1155Component-isApprovedForAll)`isApprovedForAll(self: @ContractState, owner: ContractAddress, operator: ContractAddress) -> bool` external + +See [ERC1155Component::is\_approved\_for\_all](#ERC1155Component-is_approved_for_all). + +#### [](#internal_functions)Internal functions + +#### [](#ERC1155Component-initializer)`initializer(ref self: ContractState, base_uri: ByteArray)` internal + +Initializes the contract by setting the token’s base URI as `base_uri`, and registering the supported interfaces. This should only be used inside the contract’s constructor. + +Most ERC1155 contracts expose the [IERC1155MetadataURI](#IERC1155MetadataURI) interface which is what this initializer is meant to support. If the contract DOES NOT expose the [IERC1155MetadataURI](#IERC1155MetadataURI) interface, meaning tokens do not have a URI, the contract must instead use [initializer\_no\_metadata](#ERC1155Component-initializer_no_metadata) in the constructor. Failure to abide by these instructions can lead to unexpected issues especially with UIs. + +#### [](#ERC1155Component-initializer_no_metadata)`initializer_no_metadata(ref self: ContractState)` internal + +Initializes the contract with no metadata by registering only the IERC1155 interface. + +This initializer should ONLY be used during construction in the very specific instance when the contract does NOT expose the [IERC1155MetadataURI](#IERC1155MetadataURI) interface. Initializing a contract with this initializer means that tokens will not have a URI. + +#### [](#ERC1155Component-mint_with_acceptance_check)`mint_with_acceptance_check(ref self: ContractState, to: ContractAddress, token_id: u256, value: u256, data: Span)` internal + +Creates a `value` amount of tokens of type `token_id`, and assigns them to `to`. + +Requirements: + +- `to` cannot be the zero address. +- If `to` refers to a smart contract, it must implement `IERC1155Receiver::on_ERC1155_received` and return the acceptance magic value. + +Emits a [TransferSingle](#ERC1155Component-TransferSingle) event. + +#### [](#ERC1155Component-batch_mint_with_acceptance_check)`batch_mint_with_acceptance_check(ref self: ContractState, to: ContractAddress, token_ids: Span, values: Span, data: Span)` internal + +Batched version of [mint\_with\_acceptance\_check](#ERC1155Component-mint_with_acceptance_check). + +Requirements: + +- `to` cannot be the zero address. +- `token_ids` and `values` must have the same length. +- If `to` refers to a smart contract, it must implement `IERC1155Receiver::on_ERC1155_batch_received` and return the acceptance magic value. + +Emits a [TransferBatch](#ERC1155Component-TransferBatch) event. + +#### [](#ERC1155Component-burn)`burn(ref self: ContractState, from: ContractAddress, token_id: u256, value: u256)` internal + +Destroys a `value` amount of tokens of type `token_id` from `from`. + +Requirements: + +- `from` cannot be the zero address. +- `from` must have at least `value` amount of tokens of type `token_id`. + +Emits a [TransferSingle](#ERC1155Component-TransferSingle) event. + +#### [](#ERC1155Component-batch_burn)`batch_burn(ref self: ContractState, from: ContractAddress, token_ids: Span, values: Span)` internal + +Batched version of [burn](#ERC1155Component-burn). + +Requirements: + +- `from` cannot be the zero address. +- `from` must have at least `value` amount of tokens of type `token_id`. +- `token_ids` and `values` must have the same length. + +Emits a [TransferBatch](#ERC1155Component-TransferBatch) event. + +#### [](#ERC1155Component-update_with_acceptance_check)`update_with_acceptance_check(ref self: ContractState, from: ContractAddress, to: ContractAddress, token_ids: Span, values: Span, data: Span)` internal + +Version of `update` that performs the token acceptance check by calling `onERC1155Received` or `onERC1155BatchReceived` in the receiver if it implements `IERC1155Receiver`, otherwise by checking if it is an account. + +Requirements: + +- `to` is either an account contract or supports the `IERC1155Receiver` interface. +- `token_ids` and `values` must have the same length. + +Emits a [TransferSingle](#ERC1155Component-TransferSingle) event if the arrays contain one element, and [TransferBatch](#ERC1155Component-TransferBatch) otherwise. + +#### [](#ERC1155Component-update)`update(ref self: ContractState, from: ContractAddress, to: ContractAddress, token_ids: Span, values: Span)` internal + +Transfers a `value` amount of tokens of type `id` from `from` to `to`. Will mint (or burn) if `from` (or `to`) is the zero address. + +Requirements: + +- `token_ids` and `values` must have the same length. + +Emits a [TransferSingle](#ERC1155Component-TransferSingle) event if the arrays contain one element, and [TransferBatch](#ERC1155Component-TransferBatch) otherwise. + +This function can be extended using the [ERC1155HooksTrait](#ERC1155Component-ERC1155HooksTrait), to add functionality before and/or after the transfer, mint, or burn. + +The ERC1155 acceptance check is not performed in this function. See [update\_with\_acceptance\_check](#ERC1155Component-update_with_acceptance_check) instead. + +#### [](#ERC1155Component-_set_base_uri)`_set_base_uri(ref self: ContractState, base_uri: ByteArray)` internal + +Sets a new URI for all token types, by relying on the token type ID substitution mechanism [specified in the EIP](https://eips.ethereum.org/EIPS/eip-1155#metadata). + +By this mechanism, any occurrence of the `id` substring in either the URI or any of the values in the JSON file at said URI will be replaced by clients with the token type ID. + +For example, the `https://token-cdn-domain/\id\.json` URI would be interpreted by clients as `https://token-cdn-domain/000000000000...000000000000004cce0.json` for token type ID `0x4cce0`. + +Because these URIs cannot be meaningfully represented by the `URI` event, this function emits no events. + +#### [](#events_2)Events + +#### [](#ERC1155Component-TransferSingle)`TransferSingle(operator: ContractAddress, from: ContractAddress, to: ContractAddress, id: u256, value: u256)` event + +See [IERC1155::TransferSingle](#IERC1155-TransferSingle). + +#### [](#ERC1155Component-TransferBatch)`TransferBatch(operator: ContractAddress, from: ContractAddress, to: ContractAddress, ids: Span, values: Span)` event + +See [IERC1155::TransferBatch](#IERC1155-TransferBatch). + +#### [](#ERC1155Component-ApprovalForAll)`ApprovalForAll(owner: ContractAddress, operator: ContractAddress, approved: bool)` event + +See [IERC1155::ApprovalForAll](#IERC1155-ApprovalForAll). + +#### [](#ERC1155Component-URI)`URI(value: ByteArray, id: u256)` event + +See [IERC1155::URI](#IERC1155-URI). + +### [](#ERC1155ReceiverComponent)`ERC1155ReceiverComponent`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/token/src/erc1155/erc1155_receiver.cairo) + +```cairo +use openzeppelin_token::erc1155::ERC1155ReceiverComponent; +``` + +ERC1155Receiver component implementing [IERC1155Receiver](#IERC1155Receiver). + +Implementing [SRC5Component](introspection#SRC5Component) is a requirement for this component to be implemented. + +[Embeddable Mixin Implementations](../components#mixins) + +ERC1155MixinImpl + +- [`ERC1155ReceiverImpl`](#ERC1155ReceiverComponent-Embeddable-Impls-ERC1155ReceiverImpl) +- [`ERC1155ReceiverCamelImpl`](#ERC1155ReceiverComponent-Embeddable-Impls-ERC1155ReceiverCamelImpl) +- [`SRC5Impl`](introspection#SRC5Component-Embeddable-Impls-SRC5Impl) + +Embeddable Implementations + +ERC1155ReceiverImpl + +- [`on_erc1155_received(self, operator, from, token_id, value, data)`](#ERC1155ReceiverComponent-on_erc1155_received) +- [`on_erc1155_batch_received(self, operator, from, token_ids, values, data)`](#ERC1155ReceiverComponent-on_erc1155_batch_received) + +ERC1155ReceiverCamelImpl + +- [`onERC1155Received(self, operator, from, tokenId, value, data)`](#ERC1155ReceiverComponent-onERC1155Received) +- [`onERC1155BatchReceived(self, operator, from, tokenIds, values, data)`](#ERC1155ReceiverComponent-onERC1155BatchReceived) + +Internal Functions + +InternalImpl + +- [`initializer(self)`](#ERC1155ReceiverComponent-initializer) + +#### [](#embeddable_functions_2)Embeddable functions + +#### [](#ERC1155ReceiverComponent-on_erc1155_received)`on_erc1155_received(self: @ContractState, operator: ContractAddress, from: ContractAddress, token_id: u256, value: u256, data Span) -> felt252` external + +Returns the `IERC1155Receiver` interface ID. + +#### [](#ERC1155ReceiverComponent-on_erc1155_batch_received)`on_erc1155_batch_received(self: @ContractState, operator: ContractAddress, from: ContractAddress, token_ids: Span, values: Span, data Span) -> felt252` external + +Returns the `IERC1155Receiver` interface ID. + +#### [](#ERC1155ReceiverComponent-onERC1155Received)`onERC1155Received(self: @ContractState, operator: ContractAddress, from: ContractAddress, tokenId: u256, value: u256, data Span) -> felt252` external + +See [ERC1155ReceiverComponent::on\_erc1155\_received](#ERC1155ReceiverComponent-on_erc1155_received). + +#### [](#ERC1155ReceiverComponent-onERC1155BatchReceived)`onERC1155BatchReceived(self: @ContractState, operator: ContractAddress, from: ContractAddress, tokenIds: Span, values: Span, data Span) -> felt252` external + +See [ERC1155ReceiverComponent::on\_erc1155\_batch\_received](#ERC1155ReceiverComponent-on_erc1155_batch_received). + +#### [](#internal_functions_2)Internal functions + +#### [](#ERC1155ReceiverComponent-initializer)`initializer(ref self: ContractState)` internal + +Registers the `IERC1155Receiver` interface ID as supported through introspection. + +## [](#presets)Presets + +### [](#ERC1155Upgradeable)`ERC1155Upgradeable`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/presets/src/erc1155.cairo) + +```cairo +use openzeppelin_presets::ERC1155; +``` + +Upgradeable ERC1155 contract leveraging [ERC1155Component](#ERC1155Component). + +[Sierra class hash](../presets) + +0x06c8912d4397bb25c73a571bced14cedb959a7caa40b76fb0ce19a57d4a7a9c0 + +Constructor + +- [`constructor(self, base_uri, recipient, token_ids, values, owner)`](#ERC1155Upgradeable-constructor) + +Embedded Implementations + +ERC1155Component + +- [`ERC1155MixinImpl`](#ERC1155Component-Embeddable-Mixin-Impl) + +OwnableMixinImpl + +- [`OwnableMixinImpl`](access#OwnableComponent-Mixin-Impl) + +External Functions + +- [`upgrade(self, new_class_hash)`](#ERC1155Upgradeable-upgrade) + +#### [](#ERC1155Upgradeable-constructor-section)Constructor + +#### [](#ERC1155Upgradeable-constructor)`constructor(ref self: ContractState, base_uri: ByteArray, recipient: ContractAddress, token_ids: Span, values: Span, owner: ContractAddress)` constructor + +Sets the `base_uri` for all tokens and registers the supported interfaces. Mints the `values` for `token_ids` tokens to `recipient`. Assigns `owner` as the contract owner with permissions to upgrade. + +Requirements: + +- `to` is either an account contract (supporting ISRC6) or supports the `IERC1155Receiver` interface. +- `token_ids` and `values` must have the same length. + +#### [](#ERC1155Upgradeable-external-functions)External Functions + +#### [](#ERC1155Upgradeable-upgrade)`upgrade(ref self: ContractState, new_class_hash: ClassHash)` external + +Upgrades the contract to a new implementation given by `new_class_hash`. + +Requirements: + +- The caller is the contract owner. +- `new_class_hash` cannot be zero. diff --git a/content/cairo-contracts/api/erc20.mdx b/content/cairo-contracts/api/erc20.mdx new file mode 100644 index 00000000..bbca883e --- /dev/null +++ b/content/cairo-contracts/api/erc20.mdx @@ -0,0 +1,1138 @@ +--- +title: ERC20 +--- + +This module provides interfaces, presets, and utilities related to ERC20 contracts. + +For an overview of ERC20, read our [ERC20 guide](../erc20). + +## [](#interfaces)Interfaces + +Starting from version `3.x.x`, the interfaces are no longer part of the `openzeppelin_token` package. The references documented here are contained in the `openzeppelin_interfaces` package version `v2.1.0`. + +### [](#IERC20)`IERC20`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/interfaces/src/token/erc20.cairo) + +```cairo +use openzeppelin_interfaces::erc20::IERC20; +``` + +Interface of the IERC20 standard as defined in [EIP-20](https://eips.ethereum.org/EIPS/eip-20). + +Functions + +- [`total_supply()`](#IERC20-total_supply) +- [`balance_of(account)`](#IERC20-balance_of) +- [`allowance(owner, spender)`](#IERC20-allowance) +- [`transfer(recipient, amount)`](#IERC20-transfer) +- [`transfer_from(sender, recipient, amount)`](#IERC20-transfer_from) +- [`approve(spender, amount)`](#IERC20-approve) + +Events + +- [`Transfer(from, to, value)`](#IERC20-Transfer) +- [`Approval(owner, spender, value)`](#IERC20-Approval) + +#### [](#IERC20-Functions)Functions + +#### [](#IERC20-total_supply)`total_supply() → u256` external + +Returns the amount of tokens in existence. + +#### [](#IERC20-balance_of)`balance_of(account: ContractAddress) → u256` external + +Returns the amount of tokens owned by `account`. + +#### [](#IERC20-allowance)`allowance(owner: ContractAddress, spender: ContractAddress) → u256` external + +Returns the remaining number of tokens that `spender` is allowed to spend on behalf of `owner` through [transfer\_from](#transfer_from). This is zero by default. + +This value changes when [approve](#IERC20-approve) or [transfer\_from](#IERC20-transfer_from) are called. + +#### [](#IERC20-transfer)`transfer(recipient: ContractAddress, amount: u256) → bool` external + +Moves `amount` tokens from the caller’s token balance to `to`. Returns `true` on success, reverts otherwise. + +Emits a [Transfer](#IERC20-Transfer) event. + +#### [](#IERC20-transfer_from)`transfer_from(sender: ContractAddress, recipient: ContractAddress, amount: u256) → bool` external + +Moves `amount` tokens from `sender` to `recipient` using the allowance mechanism. `amount` is then deducted from the caller’s allowance. Returns `true` on success, reverts otherwise. + +Emits a [Transfer](#IERC20-Transfer) event. + +#### [](#IERC20-approve)`approve(spender: ContractAddress, amount: u256) → bool` external + +Sets `amount` as the allowance of `spender` over the caller’s tokens. Returns `true` on success, reverts otherwise. + +Emits an [Approval](#ERC20-Approval) event. + +#### [](#IERC20-Events)Events + +#### [](#IERC20-Transfer)`Transfer(from: ContractAddress, to: ContractAddress, value: u256)` event + +Emitted when `value` tokens are moved from one address (`from`) to another (`to`). + +Note that `value` may be zero. + +#### [](#IERC20-Approval)`Approval(owner: ContractAddress, spender: ContractAddress, value: u256)` event + +Emitted when the allowance of a `spender` for an `owner` is set. `value` is the new allowance. + +### [](#IERC20Metadata)`IERC20Metadata`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/interfaces/src/token/erc20.cairo) + +```cairo +use openzeppelin_interfaces::erc20::IERC20Metadata; +``` + +Interface for the optional metadata functions in [EIP-20](https://eips.ethereum.org/EIPS/eip-20). + +Functions + +- [`name()`](#IERC20Metadata-name) +- [`symbol()`](#IERC20Metadata-symbol) +- [`decimals()`](#IERC20Metadata-decimals) + +#### [](#IERC20Metadata-Functions)Functions + +#### [](#IERC20Metadata-name)`name() → ByteArray` external + +Returns the name of the token. + +#### [](#IERC20Metadata-symbol)`symbol() → ByteArray` external + +Returns the ticker symbol of the token. + +#### [](#IERC20Metadata-decimals)`decimals() → u8` external + +Returns the number of decimals the token uses - e.g. `8` means to divide the token amount by `100000000` to get its user-readable representation. + +For example, if `decimals` equals `2`, a balance of `505` tokens should be displayed to a user as `5.05` (`505 / 10 ** 2`). + +Tokens usually opt for a value of `18`, imitating the relationship between Ether and Wei. This is the default value returned by this function. To create a custom decimals implementation, see [Customizing decimals](../erc20#customizing_decimals). + +This information is only used for *display* purposes: it in no way affects any of the arithmetic of the contract. + +### [](#IERC20Permit)`IERC20Permit`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/interfaces/src/token/erc20.cairo) + +```cairo +use openzeppelin_interfaces::erc20::IERC20Permit; +``` + +Interface of the ERC20Permit standard to support gasless token approvals as defined in [EIP-2612](https://eips.ethereum.org/EIPS/eip-2612). + +Functions + +- [`permit(owner, spender, amount, deadline, signature)`](#IERC20Permit-permit) +- [`nonces(owner)`](#IERC20Permit-nonces) +- [`DOMAIN_SEPARATOR()`](#IERC20Permit-DOMAIN_SEPARATOR) + +#### [](#IERC20Permit-Functions)Functions + +#### [](#IERC20Permit-permit)`permit(owner: ContractAddress, spender: ContractAddress, amount: u256, deadline: u64, signature: Span)` external + +Sets `amount` as the allowance of `spender` over `owner`'s tokens after validating the signature. + +#### [](#IERC20Permit-nonces)`nonces(owner: ContractAddress) → felt252` external + +Returns the current nonce of `owner`. A nonce value must be included whenever a signature for `permit` call is generated. + +#### [](#IERC20Permit-DOMAIN_SEPARATOR)`DOMAIN_SEPARATOR() → felt252` external + +Returns the domain separator used in generating a message hash for `permit` signature. The domain hashing logic follows the [SNIP12](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-12.md) standard. + +### [](#IERC4626)`IERC4626`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/interfaces/src/token/erc4626.cairo) + +```cairo +use openzeppelin_interfaces::erc4626::IERC4626; +``` + +Interface of the IERC4626 standard as defined in [EIP-4626](https://eips.ethereum.org/EIPS/eip-4626). + +Functions + +- [`asset()`](#IERC4626-asset) +- [`total_assets()`](#IERC4626-total_assets) +- [`convert_to_shares(assets)`](#IERC4626-convert_to_shares) +- [`convert_to_assets(shares)`](#IERC4626-convert_to_assets) +- [`max_deposit(receiver)`](#IERC4626-max_deposit) +- [`preview_deposit(assets)`](#IERC4626-preview_deposit) +- [`deposit(assets, receiver)`](#IERC4626-deposit) +- [`max_mint(receiver)`](#IERC4626-max_mint) +- [`preview_mint(shares)`](#IERC4626-preview_mint) +- [`mint(shares, receiver)`](#IERC4626-mint) +- [`max_withdraw(owner)`](#IERC4626-max_withdraw) +- [`preview_withdraw(assets)`](#IERC4626-preview_withdraw) +- [`withdraw(assets, receiver, owner)`](#IERC4626-withdraw) +- [`max_redeem(owner)`](#IERC4626-max_redeem) +- [`preview_redeem(shares)`](#IERC4626-preview_redeem) +- [`redeem(shares, receiver, owner)`](#IERC4626-redeem) + +Events + +- [`Deposit(sender, owner, assets, shares)`](#IERC4626-Deposit) +- [`Withdraw(sender, receiver, owner, assets, shares)`](#IERC4626-Withdraw) + +#### [](#IERC4626-Functions)Functions + +#### [](#IERC4626-asset)`asset() → ContractAddress` external + +Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing. + +Requirements: + +- MUST be an ERC20 token contract. +- MUST NOT panic. + +#### [](#IERC4626-total_assets)`total_assets() → u256` external + +Returns the total amount of the underlying asset that is “managed” by Vault. + +Requirements: + +- SHOULD include any compounding that occurs from yield. +- MUST be inclusive of any fees that are charged against assets in the Vault. +- MUST NOT panic. + +#### [](#IERC4626-convert_to_shares)`convert_to_shares(assets: u256) → u256` external + +Returns the amount of shares that the Vault would exchange for the amount of `assets` provided irrespective of slippage or fees. + +Requirements: + +- MUST NOT be inclusive of any fees that are charged against assets in the Vault. +- MUST NOT show any variations depending on the caller. +- MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. +- MUST NOT panic unless due to integer overflow caused by an unreasonably large input. +- MUST round down towards 0. + +This calculation MAY NOT reflect the "per-user" price-per-share, and instead should reflect the "average-user’s" price-per-share, meaning what the average user should expect to see when exchanging to and from. + +#### [](#IERC4626-convert_to_assets)`convert_to_assets(shares: u256) → u256` external + +Returns the amount of assets that the Vault would exchange for the amount of `shares` provided irrespective of slippage or fees. + +Requirements: + +- MUST NOT be inclusive of any fees that are charged against assets in the Vault. +- MUST NOT show any variations depending on the caller. +- MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. +- MUST NOT panic unless due to integer overflow caused by an unreasonably large input. +- MUST round down towards 0. + +This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and from. + +#### [](#IERC4626-max_deposit)`max_deposit(receiver: ContractAddress) → u256` external + +Returns the maximum amount of the underlying asset that can be deposited into the Vault for `receiver`, through a deposit call. + +Requirements: + +- MUST return a limited value if receiver is subject to some deposit limit. +- MUST return 2 \** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited. +- MUST NOT panic. + +#### [](#IERC4626-preview_deposit)`preview_deposit(assets: u256) → u256` external + +Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given current on-chain conditions. + +Requirements: + +- MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit call in the same transaction i.e. [IERC4626::deposit](#IERC4626-deposit) should return the same or more shares as `preview_deposit` if called in the same transaction. +- MUST NOT account for deposit limits like those returned from [IERC4626::max\_deposit](#IERC4626-max_deposit) and should always act as though the deposit would be accepted, regardless if the user has enough tokens approved, etc. +- MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. +- MUST NOT panic. + +Any unfavorable discrepancy between [IERC4626::convert\_to\_shares](#IERC4626-convert_to_shares) and `preview_deposit` SHOULD be considered slippage in share price or some other type of condition, meaning the depositor will lose assets by depositing. + +#### [](#IERC4626-deposit)`deposit(assets: u256, receiver: ContractAddress) → u256` external + +Mints Vault shares to `receiver` by depositing exactly amount of `assets`. + +Requirements: + +- MUST emit the [IERC4626::Deposit](#IERC4626-Deposit) event. +- MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the deposit execution, and are accounted for during deposit. +- MUST panic if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not approving enough underlying tokens to the Vault contract, etc). + +Most implementations will require pre-approval of the Vault with the Vault’s underlying asset token. + +#### [](#IERC4626-max_mint)`max_mint(receiver: ContractAddress) → u256` external + +Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call. + +Requirements: + +- MUST return a limited value if receiver is subject to some mint limit. +- MUST return 2 \** 256 - 1 if there is no limit on the maximum amount of shares that may be minted. +- MUST NOT panic. + +#### [](#IERC4626-preview_mint)`preview_mint(shares: u256) → u256` external + +Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given current on-chain conditions. + +Requirements: + +- MUST return as close to and no fewer than the exact amount of assets that would be deposited in a `mint` call in the same transaction. I.e. [IERC4626::mint](#IERC4626-mint) should return the same or fewer assets as `preview_mint` if called in the same transaction. +- MUST NOT account for mint limits like those returned from [IERC4626::max\_mint](#IERC4626-max_mint) and should always act as though the mint would be accepted, regardless if the user has enough tokens approved, etc. +- MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. +- MUST NOT panic. + +Any unfavorable discrepancy between [IERC4626::convert\_to\_assets](#IERC4626-convert_to_assets) and `preview_mint` SHOULD be considered slippage in share price or some other type of condition, meaning the depositor will lose assets by minting. + +#### [](#IERC4626-mint)`mint(shares: u256, receiver: ContractAddress) → u256` external + +Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens. + +Requirements: + +- MUST emit the [IERC4626::Deposit](#IERC4626-Deposit) event. +- MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint execution, and are accounted for during mint. +- MUST panic if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not approving enough underlying tokens to the Vault contract, etc). + +Most implementations will require pre-approval of the Vault with the Vault’s underlying asset token. + +#### [](#IERC4626-max_withdraw)`max_withdraw(owner: ContractAddress) → u256` external + +Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the Vault, through a withdraw call. + +Requirements: + +- MUST return a limited value if owner is subject to some withdrawal limit or timelock. +- MUST NOT panic. + +#### [](#IERC4626-preview_withdraw)`preview_withdraw(assets: u256) → u256` external + +Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block, given current on-chain conditions. + +Requirements: + +- MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw call in the same transaction i.e. [IERC4626::withdraw](#IERC4626-withdraw) should return the same or fewer shares as `preview_withdraw` if called in the same transaction. +- MUST NOT account for withdrawal limits like those returned from [IERC4626::max\_withdraw](#IERC4626-max_withdraw) and should always act as though the withdrawal would be accepted, regardless if the user has enough shares, etc. +- MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. +- MUST NOT panic. + +Any unfavorable discrepancy between [IERC4626::convert\_to\_shares](#IERC4626-convert_to_shares) and `preview_withdraw` SHOULD be considered slippage in share price or some other type of condition, meaning the depositor will lose assets by depositing. + +#### [](#IERC4626-withdraw)`withdraw(assets: u256, receiver: ContractAddress, owner: ContractAddress) → u256` external + +Burns shares from owner and sends exactly assets of underlying tokens to receiver. + +Requirements: + +- MUST emit the [IERC4626::Withdraw](#IERC4626-Withdraw) event. +- MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the withdraw execution, and are accounted for during withdraw. +- MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner not having enough shares, etc). + +Some implementations will require pre-requesting to the Vault before a withdrawal may be performed. Those methods should be performed separately. + +#### [](#IERC4626-max_redeem)`max_redeem(owner: ContractAddress) → u256` external + +Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault, through a redeem call. + +Requirements: + +- MUST return a limited value if owner is subject to some withdrawal limit or timelock. +- MUST return `ERC20::balance_of(owner)` if `owner` is not subject to any withdrawal limit or timelock. +- MUST NOT panic. + +#### [](#IERC4626-preview_redeem)`preview_redeem(shares: u256) → u256` external + +Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block, given current on-chain conditions. + +Requirements: + +- MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call in the same transaction i.e. [IERC4626::redeem](#IERC4626-redeem) should return the same or more assets as preview\_redeem if called in the same transaction. +- MUST NOT account for redemption limits like those returned from [IERC4626::max\_redeem](#IERC4626-max_redeem) and should always act as though the redemption would be accepted, regardless if the user has enough shares, etc. +- MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. +- MUST NOT panic. + +Any unfavorable discrepancy between [IERC4626::convert\_to\_assets](#IERC4626-convert_to_assets) and `preview_redeem` SHOULD be considered slippage in share price or some other type of condition, meaning the depositor will lose assets by redeeming. + +#### [](#IERC4626-redeem)`redeem(shares: u256, receiver: ContractAddress, owner: ContractAddress) → u256` external + +Burns exactly shares from owner and sends assets of underlying tokens to receiver. + +Requirements: + +- MUST emit the [IERC4626::Withdraw](#IERC4626-Withdraw) event. +- MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the redeem execution, and are accounted for during redeem. +- MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner not having enough shares, etc). + +Some implementations will require pre-requesting to the Vault before a withdrawal may be performed. Those methods should be performed separately. + +#### [](#IERC4626-Events)Events + +#### [](#IERC4626-Deposit)`Deposit(sender: ContractAddress, owner: ContractAddress, assets: u256, shares: u256)` event + +Emitted when `sender` exchanges `assets` for `shares` and transfers those `shares` to `owner`. + +#### [](#IERC4626-Withdraw)`Withdraw(sender: ContractAddress, receiver: ContractAddress, owner: ContractAddress, assets: u256, shares: u256)` event + +Emitted when `sender` exchanges `shares`, owned by `owner`, for `assets` and transfers those `assets` to `receiver`. + +## [](#core)Core + +### [](#ERC20Component)`ERC20Component`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/token/src/erc20/erc20.cairo) + +```cairo +use openzeppelin_token::erc20::ERC20Component; +``` + +ERC20 component extending [IERC20](#IERC20) and [IERC20Metadata](#IERC20Metadata). + +See [Hooks](#ERC20Component-Hooks) to understand how are hooks used. + +Hooks + +ERC20HooksTrait + +- [`before_update(self, from, recipient, amount)`](#ERC20Component-before_update) +- [`after_update(self, from, recipient, amount)`](#ERC20Component-after_update) + +[Embeddable Mixin Implementations](../components#mixins) + +ERC20MixinImpl + +- [`ERC20Impl`](#ERC20Component-Embeddable-Impls-ERC20Impl) +- [`ERC20MetadataImpl`](#ERC20Component-Embeddable-Impls-ERC20MetadataImpl) +- [`ERC20CamelOnlyImpl`](#ERC20Component-Embeddable-Impls-ERC20CamelOnlyImpl) + +Embeddable Implementations + +ERC20Impl + +- [`total_supply(self)`](#ERC20Component-total_supply) +- [`balance_of(self, account)`](#ERC20Component-balance_of) +- [`allowance(self, owner, spender)`](#ERC20Component-allowance) +- [`transfer(self, recipient, amount)`](#ERC20Component-transfer) +- [`transfer_from(self, sender, recipient, amount)`](#ERC20Component-transfer_from) +- [`approve(self, spender, amount)`](#ERC20Component-approve) + +ERC20MetadataImpl + +- [`name(self)`](#ERC20Component-name) +- [`symbol(self)`](#ERC20Component-symbol) +- [`decimals(self)`](#ERC20Component-decimals) + +ERC20CamelOnlyImpl + +- [`totalSupply(self)`](#ERC20Component-totalSupply) +- [`balanceOf(self, account)`](#ERC20Component-balanceOf) +- [`transferFrom(self, sender, recipient, amount)`](#ERC20Component-transferFrom) + +ERC20PermitImpl + +- [`permit(self, owner, spender, amount, deadline, signature)`](#ERC20Component-permit) +- [`nonces(self, owner)`](#ERC20Component-nonces) +- [`DOMAIN_SEPARATOR(self)`](#ERC20Component-DOMAIN_SEPARATOR) + +SNIP12MetadataExternalImpl + +- [`snip12_metadata(self)`](#ERC20Component-snip12_metadata) + +Internal implementations + +InternalImpl + +- [`initializer(self, name, symbol)`](#ERC20Component-initializer) +- [`mint(self, recipient, amount)`](#ERC20Component-mint) +- [`burn(self, account, amount)`](#ERC20Component-burn) +- [`update(self, from, to, amount)`](#ERC20Component-update) +- [`_transfer(self, sender, recipient, amount)`](#ERC20Component-_transfer) +- [`_approve(self, owner, spender, amount)`](#ERC20Component-_approve) +- [`_spend_allowance(self, owner, spender, amount)`](#ERC20Component-_spend_allowance) + +Events + +- [`Transfer(from, to, value)`](#ERC20Component-Transfer) +- [`Approval(owner, spender, value)`](#ERC20Component-Approval) + +#### [](#ERC20Component-Hooks)Hooks + +Hooks are functions which implementations can extend the functionality of the component source code. Every contract using ERC20Component is expected to provide an implementation of the ERC20HooksTrait. For basic token contracts, an empty implementation with no logic must be provided. + +You can use `openzeppelin_token::erc20::ERC20HooksEmptyImpl` which is already available as part of the library for this purpose. + +#### [](#ERC20Component-before_update)`before_update(ref self: ContractState, from: ContractAddress, recipient: ContractAddress, amount: u256)` hook + +Function executed at the beginning of the [update](#ERC20Component-update) function prior to any other logic. + +#### [](#ERC20Component-after_update)`after_update(ref self: ContractState, from: ContractAddress, recipient: ContractAddress, amount: u256)` hook + +Function executed at the end of the [update](#ERC20Component-update) function. + +#### [](#ERC20Component-Embeddable-functions)Embeddable functions + +#### [](#ERC20Component-total_supply)`total_supply(@self: ContractState) → u256` external + +See [IERC20::total\_supply](#IERC20-total_supply). + +#### [](#ERC20Component-balance_of)`balance_of(@self: ContractState, account: ContractAddress) → u256` external + +See [IERC20::balance\_of](#IERC20-balance_of). + +#### [](#ERC20Component-allowance)`allowance(@self: ContractState, owner: ContractAddress, spender: ContractAddress) → u256` external + +See [IERC20::allowance](#IERC20-allowance). + +#### [](#ERC20Component-transfer)`transfer(ref self: ContractState, recipient: ContractAddress, amount: u256) → bool` external + +See [IERC20::transfer](#IERC20-transfer). + +Requirements: + +- `recipient` cannot be the zero address. +- The caller must have a balance of at least `amount`. + +#### [](#ERC20Component-transfer_from)`transfer_from(ref self: ContractState, sender: ContractAddress, recipient: ContractAddress, amount: u256) → bool` external + +See [IERC20::transfer\_from](#IERC20-transfer_from). + +Requirements: + +- `sender` cannot be the zero address. +- `sender` must have a balance of at least `amount`. +- `recipient` cannot be the zero address. +- The caller must have allowance for `sender`'s tokens of at least `amount`. + +#### [](#ERC20Component-approve)`approve(ref self: ContractState, spender: ContractAddress, amount: u256) → bool` external + +See [IERC20::approve](#IERC20-approve). + +Requirements: + +- `spender` cannot be the zero address. + +#### [](#ERC20Component-name)`name() → ByteArray` external + +See [IERC20Metadata::name](#IERC20Metadata-name). + +#### [](#ERC20Component-symbol)`symbol() → ByteArray` external + +See [IERC20Metadata::symbol](#IERC20Metadata-symbol). + +#### [](#ERC20Component-decimals)`decimals() → u8` external + +See [IERC20Metadata::decimals](#IERC20Metadata-decimals). + +#### [](#ERC20Component-totalSupply)`totalSupply(self: @ContractState) → u256` external + +See [IERC20::total\_supply](#IERC20-total_supply). + +Supports the Cairo v0 convention of writing external methods in camelCase as discussed [here](https://github.com/OpenZeppelin/cairo-contracts/discussions/34). + +#### [](#ERC20Component-balanceOf)`balanceOf(self: @ContractState, account: ContractAddress) → u256` external + +See [IERC20::balance\_of](#IERC20-balance_of). + +Supports the Cairo v0 convention of writing external methods in camelCase as discussed [here](https://github.com/OpenZeppelin/cairo-contracts/discussions/34). + +#### [](#ERC20Component-transferFrom)`transferFrom(ref self: ContractState, sender: ContractAddress, recipient: ContractAddress) → bool` external + +See [IERC20::transfer\_from](#IERC20-transfer_from). + +Supports the Cairo v0 convention of writing external methods in camelCase as discussed [here](https://github.com/OpenZeppelin/cairo-contracts/discussions/34). + +#### [](#ERC20Component-permit)`permit(ref self: ContractState, owner: ContractAddress, spender: ContractAddress, amount: u256, deadline: u64, signature: Span) → bool` external + +Sets `amount` as the allowance of `spender` over `owner`'s tokens after validating the signature. + +Requirements: + +- `owner` is a deployed account contract. +- `spender` is not the zero address. +- `deadline` is not a timestamp in the past. +- `signature` is a valid signature that can be validated with a call to `owner` account. +- `signature` must use the current nonce of the `owner`. + +Emits an [Approval](#ERC20-Approval) event. Every successful call increases \`owner’s nonce by one. + +#### [](#ERC20Component-nonces)`nonces(self: @ContractState, owner: ContractAddress) → felt252` external + +Returns the current nonce of `owner`. A nonce value must be included whenever a signature for `permit` call is generated. + +#### [](#ERC20Component-DOMAIN_SEPARATOR)`DOMAIN_SEPARATOR(self: @ContractState) → felt252` external + +Returns the domain separator used in generating a message hash for `permit` signature. The domain hashing logic follows the [SNIP12](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-12.md) standard. + +#### [](#ERC20Component-snip12_metadata)`snip12_metadata(self: @ContractState) → (felt252, felt252)` external + +Returns the domain name and version used to generate the message hash for permit signature. + +The returned tuple contains: + +- `t.0`: The name used in the [SNIP12Metadata](utilities#snip12) implementation. +- `t.1`: The version used in the [SNIP12Metadata](utilities#snip12) implementation. + +#### [](#ERC20Component-Internal-functions)Internal functions + +#### [](#ERC20Component-initializer)`initializer(ref self: ContractState, name: ByteArray, symbol: ByteArray)` internal + +Initializes the contract by setting the token name and symbol. This should be used inside of the contract’s constructor. + +#### [](#ERC20Component-mint)`mint(ref self: ContractState, recipient: ContractAddress, amount: u256)` internal + +Creates an `amount` number of tokens and assigns them to `recipient`. + +Emits a [Transfer](#ERC20Component-Transfer) event with `from` being the zero address. + +Requirements: + +- `recipient` cannot be the zero address. + +#### [](#ERC20Component-burn)`burn(ref self: ContractState, account: ContractAddress, amount: u256)` internal + +Destroys `amount` number of tokens from `account`. + +Emits a [Transfer](#ERC20Component-Transfer) event with `to` set to the zero address. + +Requirements: + +- `account` cannot be the zero address. + +#### [](#ERC20Component-update)`update(ref self: ContractState, from: ContractAddress, to: ContractAddress, amount: u256)` internal + +Transfers an `amount` of tokens from `from` to `to`, or alternatively mints (or burns) if `from` (or `to`) is the zero address. + +This function can be extended using the [ERC20HooksTrait](#ERC20Component-ERC20HooksTrait), to add functionality before and/or after the transfer, mint, or burn. + +Emits a [Transfer](#ERC20Component-Transfer) event. + +#### [](#ERC20Component-_transfer)`_transfer(ref self: ContractState, sender: ContractAddress, recipient: ContractAddress, amount: u256)` internal + +Moves `amount` of tokens from `from` to `to`. + +This internal function does not check for access permissions but can be useful as a building block, for example to implement automatic token fees, slashing mechanisms, etc. + +Emits a [Transfer](#ERC20Component-Transfer) event. + +Requirements: + +- `from` cannot be the zero address. +- `to` cannot be the zero address. +- `from` must have a balance of at least `amount`. + +#### [](#ERC20Component-_approve)`_approve(ref self: ContractState, owner: ContractAddress, spender: ContractAddress, amount: u256)` internal + +Sets `amount` as the allowance of `spender` over `owner`'s tokens. + +This internal function does not check for access permissions but can be useful as a building block, for example to implement automatic allowances on behalf of other addresses. + +Emits an [Approval](#ERC20Component-Approval) event. + +Requirements: + +- `owner` cannot be the zero address. +- `spender` cannot be the zero address. + +#### [](#ERC20Component-_spend_allowance)`_spend_allowance(ref self: ContractState, owner: ContractAddress, spender: ContractAddress, amount: u256)` internal + +Updates `owner`'s allowance for `spender` based on spent `amount`. + +This internal function does not update the allowance value in the case of infinite allowance. + +Possibly emits an [Approval](#ERC20Component-Approval) event. + +#### [](#ERC20Component-Events)Events + +#### [](#ERC20Component-Transfer)`Transfer(from: ContractAddress, to: ContractAddress, value: u256)` event + +See [IERC20::Transfer](#IERC20-Transfer). + +#### [](#ERC20Component-Approval)`Approval(owner: ContractAddress, spender: ContractAddress, value: u256)` event + +See [IERC20::Approval](#IERC20-Approval). + +## [](#extensions)Extensions + +### [](#ERC4626Component)`ERC4626Component`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/token/src/erc20/extensions/erc4626/interface.cairo#L19) + +```cairo +use openzeppelin_token::erc20::extensions::erc4626::ERC4626Component; +``` + +Extension of ERC20 that implements the [IERC4626](#IERC4626) interface which allows the minting and burning of "shares" in exchange for an underlying "asset." The component leverages traits to configure fees, limits, and decimals. + +[Immutable Component Config](../components#immutable_config) + +constants + +- [`UNDERLYING_DECIMALS`](#ERC4626Component-IC-UNDERLYING_DECIMALS) +- [`DECIMALS_OFFSET`](#ERC4626Component-IC-DECIMALS_OFFSET) + +functions + +- [`validate()`](#ERC4626Component-IC-validate) + +Hooks + +FeeConfigTrait + +- [`calculate_deposit_fee(self, assets, shares)`](#ERC4626Component-calculate_deposit_fee) +- [`calculate_mint_fee(self, assets, shares)`](#ERC4626Component-calculate_mint_fee) +- [`calculate_withdraw_fee(self, assets, shares)`](#ERC4626Component-calculate_withdraw_fee) +- [`calculate_redeem_fee(self, assets, shares)`](#ERC4626Component-calculate_redeem_fee) + +LimitConfigTrait + +- [`deposit_limit(self, receiver)`](#ERC4626Component-deposit_limit) +- [`mint_limit(self, receiver)`](#ERC4626Component-mint_limit) +- [`withdraw_limit(self, owner)`](#ERC4626Component-withdraw_limit) +- [`redeem_limit(self, owner)`](#ERC4626Component-redeem_limit) + +ERC4626HooksTrait + +- [`before_deposit(self, caller, receiver, assets, shares, fee)`](#ERC4626Component-before_deposit) +- [`after_deposit(self, caller, receiver, assets, shares, fee)`](#ERC4626Component-after_deposit) +- [`before_withdraw(self, caller, receiver, owner, assets, shares, fee)`](#ERC4626Component-before_withdraw) +- [`after_withdraw(self, caller, receiver, owner, assets, shares, fee)`](#ERC4626Component-after_withdraw) + +AssetsManagementTrait + +- [`get_total_assets(self)`](#ERC4626Component-get_total_assets) +- [`transfer_assets_in(self, from, assets)`](#ERC4626Component-transfer_assets_in) +- [`transfer_assets_out(self, to, assets)`](#ERC4626Component-transfer_assets_out) + +Embeddable Implementations + +ERC4626Impl + +- [`asset(self)`](#ERC4626Component-asset) +- [`total_assets(self)`](#ERC4626Component-total_assets) +- [`convert_to_shares(self, assets)`](#ERC4626Component-convert_to_shares) +- [`convert_to_assets(self, shares)`](#ERC4626Component-convert_to_assets) +- [`max_deposit(self, receiver)`](#ERC4626Component-max_deposit) +- [`preview_deposit(self, assets)`](#ERC4626Component-preview_deposit) +- [`deposit(self, assets, receiver)`](#ERC4626Component-deposit) +- [`max_mint(self, receiver)`](#ERC4626Component-max_mint) +- [`preview_mint(self, shares)`](#ERC4626Component-preview_mint) +- [`mint(self, shares, receiver)`](#ERC4626Component-mint) +- [`max_withdraw(self, owner)`](#ERC4626Component-max_withdraw) +- [`preview_withdraw(self, assets)`](#ERC4626Component-preview_withdraw) +- [`withdraw(self, assets, receiver, owner)`](#ERC4626Component-withdraw) +- [`max_redeem(self, owner)`](#ERC4626Component-max_redeem) +- [`preview_redeem(self, shares)`](#ERC4626Component-preview_redeem) +- [`redeem(self, shares, receiver, owner)`](#ERC4626Component-redeem) + +ERC20Impl + +- [`total_supply(self)`](#ERC20Component-total_supply) +- [`balance_of(self, account)`](#ERC20Component-balance_of) +- [`allowance(self, owner, spender)`](#ERC20Component-allowance) +- [`transfer(self, recipient, amount)`](#ERC20Component-transfer) +- [`transfer_from(self, sender, recipient, amount)`](#ERC20Component-transfer_from) +- [`approve(self, spender, amount)`](#ERC20Component-approve) + +ERC4626MetadataImpl + +- [`name(self)`](#ERC4626Component-name) +- [`symbol(self)`](#ERC4626Component-symbol) +- [`decimals(self)`](#ERC4626Component-decimals) + +Internal functions + +InternalImpl + +- [`initializer(self, asset_address)`](#ERC4626Component-initializer) +- [`_deposit(self, caller, receiver, assets, shares)`](#ERC4626Component-_deposit) +- [`_withdraw(self, caller, receiver, owner, assets, shares)`](#ERC4626Component-_withdraw) +- [`_convert_to_shares(self, assets, rounding)`](#ERC4626Component-_convert_to_shares) +- [`_convert_to_assets(self, shares, rounding)`](#ERC4626Component-_convert_to_assets) + +#### [](#ERC4626Component-Immutable-Config)Immutable Config + +#### [](#ERC4626Component-IC-UNDERLYING_DECIMALS)`UNDERLYING_DECIMALS: u128` constant + +Should match the underlying asset’s decimals. The default value is `18`. + +#### [](#ERC4626Component-IC-DECIMALS_OFFSET)`DECIMALS_OFFSET: u128` constant + +Corresponds to the representational offset between `UNDERLYING_DECIMALS` and the vault decimals. The greater the offset, the more expensive it is for attackers to execute an inflation attack. + +#### [](#ERC4626Component-IC-validate)`validate()` internal + +Validates the given implementation of the contract’s configuration. + +Requirements: + +- `UNDERLYING_DECIMALS` + `DECIMALS_OFFSET` cannot exceed 255 (max u8). + +This function is called by the contract’s initializer. + +#### [](#ERC4626Component-Hooks)Hooks + +Hooks are functions which implementations can extend the functionality of the component source code. Every contract using ERC4626Component is expected to provide an implementation of the ERC4626HooksTrait. For basic token contracts, an empty implementation with no logic must be provided. + +You can use `openzeppelin_token::erc20::extensions::erc4626::ERC4626EmptyHooks` which is already available as part of the library for this purpose. + +#### [](#feeconfigtrait)FeeConfigTrait + +The logic for calculating entry and exit fees is expected to be defined at the contract level. Defaults to no entry or exit fees. + +The FeeConfigTrait hooks directly into the preview methods of the ERC4626 component. The preview methods must return as close to the exact amount of shares or assets as possible if the actual (previewed) operation occurred in the same transaction (according to [EIP-4626](https://eips.ethereum.org/EIPS/eip-4626) spec). All operations use their corresponding preview method as the value of assets or shares being moved to or from the user. The fees calculated in FeeConfigTrait are used to adjust the final asset and share amounts used in both the preview and the actual operations. + +To transfer fees, this trait needs to be coordinated with `ERC4626Component::ERC4626Hooks`. + +See implementation examples: + +- Contract charging fees in assets: [ERC4626AssetsFeesMock](https://github.com/OpenZeppelin/cairo-contracts/tree/main/packages/test_common/src/mocks/erc4626.cairo#L253) +- Contract charging fees in shares: [ERC4626SharesFeesMock](https://github.com/OpenZeppelin/cairo-contracts/tree/main/packages/test_common/src/mocks/erc4626.cairo#L426) + +#### [](#ERC4626Component-calculate_deposit_fee)`calculate_deposit_fee(self: @ContractState, assets: u256, shares: u256) → Option` hook + +Calculates the entry fee for a deposit during [preview\_deposit](#ERC4626Component-preview_deposit). The returned fee affects the final asset and share amounts. Fees are not transferred automatically and must be handled in the [after\_deposit](#ERC4626Component-after_deposit) hook: asset fees should be transferred from the vault’s management to the fee recipient, while share fees should be minted to the fee recipient. + +#### [](#ERC4626Component-calculate_mint_fee)`calculate_mint_fee(self: @ContractState, assets: u256, shares: u256) → Option` hook + +Calculates the entry fee for a mint during [preview\_mint](#ERC4626Component-preview_mint). The returned fee affects the final asset and share amounts. Fees are not transferred automatically and must be handled in the [after\_deposit](#ERC4626Component-after_deposit) hook: asset fees should be transferred from the vault’s management to the fee recipient, while share fees should be minted to the fee recipient. + +#### [](#ERC4626Component-calculate_withdraw_fee)`calculate_withdraw_fee(self: @ContractState, assets: u256, shares: u256) → Option` hook + +Calculates the exit fee for a withdraw during [preview\_withdraw](#ERC4626Component-preview_withdraw). The returned fee affects the final asset and share amounts. Fees are not transferred automatically and must be handled in the [before\_withdraw](#ERC4626Component-before_withdraw) hook: asset fees should be transferred from the vault’s management to the fee recipient, while share fees should be transferred from the owner to the fee recipient. + +#### [](#ERC4626Component-calculate_redeem_fee)`calculate_redeem_fee(self: @ContractState, assets: u256, shares: u256) → Option` hook + +Calculates the exit fee for a redeem during [preview\_redeem](#ERC4626Component-preview_redeem). The returned fee affects the final asset and share amounts. Fees are not transferred automatically and must be handled in the [before\_withdraw](#ERC4626Component-before_withdraw) hook: asset fees should be transferred from the vault’s management to the fee recipient, while share fees should be transferred from the owner to the fee recipient. + +#### [](#limitconfigtrait)LimitConfigTrait + +Sets limits to the target exchange type and is expected to be defined at the contract level. These limits correspond directly to the `max_` i.e. `deposit_limit` → `max_deposit`. + +The [EIP-4626](https://eips.ethereum.org/EIPS/eip-4626) spec states that the `max_` methods must take into account all global and user-specific limits. If an operation is disabled (even temporarily), the corresponding limit MUST be `0` and MUST NOT panic. + +#### [](#ERC4626Component-deposit_limit)`deposit_limit(ref self: ContractState, receiver: ContractAddress) → Option` hook + +The max deposit allowed. + +Defaults (`Option::None`) to 2 \** 256 - 1. + +#### [](#ERC4626Component-mint_limit)`mint_limit(ref self: ContractState, receiver: ContractAddress) → Option` hook + +The max mint allowed. + +Defaults (`Option::None`) to 2 \** 256 - 1. + +#### [](#ERC4626Component-withdraw_limit)`withdraw_limit(ref self: ContractState, owner: ContractAddress) → Option` hook + +The max withdraw allowed. + +Defaults (`Option::None`) to the full asset balance of `owner` converted from shares. + +#### [](#ERC4626Component-redeem_limit)`redeem_limit(ref self: ContractState, owner: ContractAddress) → Option` hook + +The max redeem allowed. + +Defaults (`Option::None`) to the full asset balance of `owner`. + +#### [](#erc4626hookstrait)ERC4626HooksTrait + +Allows contracts to hook logic into deposit and withdraw transactions. This is where contracts can transfer fees. + +ERC4626 preview methods must be inclusive of any entry or exit fees. Fees are calculated using [FeeConfigTrait](#ERC4626Component-FeeConfigTrait) methods and automatically adjust the final asset and share amounts. Fee transfers are handled in `ERC4626HooksTrait` methods. + +Special care must be taken when calling external contracts in these hooks. In that case, consider implementing reentrancy protections. For example, in the `withdraw` flow, the `withdraw_limit` is checked **before** the `before_withdraw` hook is invoked. If this hook performs a reentrant call that invokes `withdraw` again, the subsequent check on `withdraw_limit` will be done before the first withdrawal’s core logic (e.g., burning shares and transferring assets) is executed. This could lead to bypassing withdrawal constraints or draining funds. + +See the [ERC4626AssetsFeesMock](https://github.com/OpenZeppelin/cairo-contracts/tree/main/packages/test_common/src/mocks/erc4626.cairo#L253) and [ERC4626SharesFeesMock](https://github.com/OpenZeppelin/cairo-contracts/tree/main/packages/test_common/src/mocks/erc4626.cairo#L426) examples. + +#### [](#ERC4626Component-before_deposit)`before_deposit(ref self: ContractState, caller: ContractAddress, receiver: ContractAddress, assets: u256, shares: u256, fee: Option)` hook + +Hooks into [\_deposit](#ERC4626Component-_deposit). + +Executes logic before transferring assets and minting shares. The fee is calculated via [FeeConfigTrait](#ERC4626Component-FeeConfigTrait). Assets and shares represent the actual amounts the user will spend and receive, respectively. Asset fees are included in assets; share fees are excluded from shares. + +#### [](#ERC4626Component-after_deposit)`after_deposit(ref self: ContractState, caller: ContractAddress, receiver: ContractAddress, assets: u256, shares: u256, fee: Option)` hook + +Hooks into [\_deposit](#ERC4626Component-_deposit). + +Executes logic after transferring assets and minting shares. The fee is calculated via [FeeConfigTrait](#ERC4626Component-FeeConfigTrait). Assets and shares represent the actual amounts the user will spend and receive, respectively. Asset fees are included in assets; share fees are excluded from shares. + +#### [](#ERC4626Component-before_withdraw)`before_withdraw(ref self: ContractState, caller: ContractAddress, receiver: ContractAddress, owner: ContractAddress, assets: u256, shares: u256, fee: Option)` hook + +Hooks into [\_withdraw](#ERC4626Component-_withdraw). + +Executes logic before burning shares and transferring assets. The fee is calculated via [FeeConfigTrait](#ERC4626Component-FeeConfigTrait). Assets and shares represent the actual amounts the user will receive and spend, respectively. Asset fees are excluded from assets; share fees are included in shares. + +#### [](#ERC4626Component-after_withdraw)`after_withdraw(ref self: ContractState, caller: ContractAddress, receiver: ContractAddress, owner: ContractAddress, assets: u256, shares: u256, fee: Option)` hook + +Hooks into [\_withdraw](#ERC4626Component-_withdraw). + +Executes logic after burning shares and transferring assets. The fee is calculated via [FeeConfigTrait](#ERC4626Component-FeeConfigTrait). Assets and shares represent the actual amounts the user will receive and spend, respectively. Asset fees are excluded from assets; share fees are included in shares. + +#### [](#assetsmanagementtrait)AssetsManagementTrait + +Defines how the ERC4626 vault manages its underlying assets. This trait provides the core asset management functionality for the vault, abstracting the actual storage and transfer mechanisms. It enables two primary implementation patterns: + +1. **Self-managed assets**: The vault contract holds assets directly on its own address. This is the default behavior provided by `ERC4626SelfAssetsManagement` implementation. +2. **External vault**: Assets are managed by an external contract, allowing for more complex asset management strategies. The exact implementation is expected to be defined by the contract implementing the ERC4626 component. + +The trait methods are called during deposit, withdrawal, and total assets calculations, ensuring that the vault’s share pricing remains accurate regardless of the underlying asset management strategy. + +Implementations must ensure that `get_total_assets` returns the actual amount of assets that can be withdrawn by users. Inaccurate reporting can lead to incorrect share valuations and potential economic attacks. + +See implementation examples: + +- Self-managed vault: [ERC4626SelfAssetsManagement](https://github.com/OpenZeppelin/cairo-contracts/tree/main/packages/token/src/erc20/extensions/erc4626/erc4626.cairo#L760). +- External vault: [ERC4626ExternalAssetsManagement](https://github.com/OpenZeppelin/cairo-contracts/tree/main/packages/test_common/src/mocks/erc4626.cairo#L92). + +#### [](#ERC4626Component-get_total_assets)`get_total_assets(self: @ContractState) → u256` hook + +Returns the total amount of underlying assets under the vault’s management. Used for share price calculations and determining the vault’s total value. + +This method should return the actual amount of assets that the vault controls and that can be used to satisfy withdrawal requests. For self-managed vaults, this is typically the vault contract’s token balance. For external vaults, this should include any assets deposited in external protocols, minus any that are locked or unredeemable. + +The accuracy of this method is critical for proper vault operation: - Overreporting can lead to share dilution and user losses. - Underreporting can lead to share inflation and potential economic attacks. + +#### [](#ERC4626Component-transfer_assets_in)`transfer_assets_in(ref self: ContractState, from: ContractAddress, assets: u256)` hook + +Transfers assets from an external address into the vault’s management. Called during `deposit` and `mint` operations. + +This method should handle the actual transfer of underlying assets from the `from` address into the vault’s control. For self-managed vaults, this typically means transferring tokens to the vault contract’s address. For external vaults, this might involve transferring into an external contract. + +Requirements: + +- MUST transfer exactly `assets` amount of the underlying token. +- SHOULD revert if the transfer fails or insufficient allowance/balance. + +#### [](#ERC4626Component-transfer_assets_out)`transfer_assets_out(ref self: ContractState, to: ContractAddress, assets: u256)` hook + +Transfers assets from the vault’s management to an external address. Called during withdraw and redeem operations. + +This method should handle the actual transfer of underlying assets from the vault’s control to the `to` address. For self-managed vaults, this typically means transferring tokens from the vault contract’s address. For external vaults, this might involve withdrawing from an external contract first. + +Requirements: + +- MUST transfer exactly `assets` amount of the underlying token. +- SHOULD revert if insufficient assets are available or transfer fails. + +#### [](#embeddable_functions)Embeddable functions + +#### [](#ERC4626Component-asset)`asset(self: @ContractState) → ContractAddress` external + +Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing. + +#### [](#ERC4626Component-total_assets)`total_assets(self: @ContractState) → u256` external + +Returns the total amount of the underlying asset that is “managed” by Vault. + +#### [](#ERC4626Component-convert_to_shares)`convert_to_shares(self: @ContractState, assets: u256) → u256` external + +Returns the amount of shares that the Vault would exchange for the amount of assets provided irrespective of slippage or fees. + +As per the [EIP-4626](https://eips.ethereum.org/EIPS/eip-4626) spec, this may panic *only* if there’s an overflow from an unreasonably large input. + +#### [](#ERC4626Component-convert_to_assets)`convert_to_assets(self: @ContractState, shares: u256) → u256` external + +Returns the amount of assets that the Vault would exchange for the amount of shares provided irrespective of slippage or fees. + +As per the [EIP-4626](https://eips.ethereum.org/EIPS/eip-4626) spec, this may panic *only* if there’s an overflow from an unreasonably large input. + +#### [](#ERC4626Component-max_deposit)`max_deposit(self: @ContractState, receiver: ContractAddress) → u256` external + +Returns the maximum amount of the underlying asset that can be deposited into the Vault for the `receiver`, through a [deposit](#ERC4626Component-deposit) call. + +The default max deposit value is 2 \** 256 - 1. + +This can be changed in the implementing contract by defining custom logic in [LimitConfigTrait::deposit\_limit](#ERC4626Component-deposit_limit). + +#### [](#ERC4626Component-preview_deposit)`preview_deposit(self: @ContractState, assets: u256) → u256` external + +Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given current on-chain conditions. + +The default deposit preview value is the full amount of shares. This can be changed to account for fees, for example, in the implementing contract by defining custom logic in [FeeConfigTrait::calculate\_deposit\_fee](#ERC4626Component-calculate_deposit_fee). + +This method must be inclusive of entry fees to be compliant with the [EIP-4626](https://eips.ethereum.org/EIPS/eip-4626) spec. + +#### [](#ERC4626Component-deposit)`deposit(ref self: ContractState, assets: u256, receiver: ContractAddress) → u256` external + +Mints Vault shares to `receiver` by depositing exactly `assets` of underlying tokens. Returns the amount of newly-minted shares. + +Requirements: + +- `assets` is less than or equal to the max deposit amount for `receiver`. + +Emits a [Deposit](#IERC4626-Deposit) event. + +#### [](#ERC4626Component-max_mint)`max_mint(self: @ContractState, receiver: ContractAddress) → u256` external + +Returns the maximum amount of the Vault shares that can be minted for `receiver` through a [mint](#ERC4626Component-mint) call. + +The default max mint value is 2 \** 256 - 1. + +This can be changed in the implementing contract by defining custom logic in [LimitConfigTrait::mint\_limit](#ERC4626Component-mint_limit). + +#### [](#ERC4626Component-preview_mint)`preview_mint(self: @ContractState, shares: u256) → u256` external + +Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given current on-chain conditions. + +The default mint preview value is the full amount of assets. This can be changed to account for fees, for example, in the implementing contract by defining custom logic in [FeeConfigTrait::calculate\_mint\_fee](#ERC4626Component-calculate_mint_fee). + +This method must be inclusive of entry fees to be compliant with the [EIP-4626](https://eips.ethereum.org/EIPS/eip-4626) spec. + +#### [](#ERC4626Component-mint)`mint(self: @ContractState, shares: u256, receiver: ContractAddress) → u256` external + +Mints exactly Vault `shares` to `receiver` by depositing amount of underlying tokens. Returns the amount deposited assets. + +Requirements: + +- `shares` is less than or equal to the max shares amount for `receiver`. + +Emits a [Deposit](#IERC4626-Deposit) event. + +#### [](#ERC4626Component-max_withdraw)`max_withdraw(self: @ContractState, owner: ContractAddress) → u256` external + +Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the Vault, through a [withdraw](#ERC4626Component-withdraw) call. + +The default max withdraw value is the full balance of assets for `owner` (converted from shares). This can be changed in the implementing contract by defining custom logic in [LimitConfigTrait::withdraw\_limit](#ERC4626Component-withdraw_limit). + +With customized limits, the maximum withdraw amount will either be the custom limit itself or `owner`'s total asset balance, whichever value is less. + +#### [](#ERC4626Component-preview_withdraw)`preview_withdraw(self: @ContractState, assets: u256) → u256` external + +Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block, given current on-chain conditions. + +The default withdraw preview value is the full amount of shares. This can be changed to account for fees, for example, in the implementing contract by defining custom logic in [FeeConfigTrait::calculate\_withdraw\_fee](#ERC4626Component-calculate_withdraw_fee). + +This method must be inclusive of exit fees to be compliant with the [EIP-4626](https://eips.ethereum.org/EIPS/eip-4626) spec. + +#### [](#ERC4626Component-withdraw)`withdraw(self: @ContractState, assets: u256, receiver: ContractAddress, owner: ContractAddress) → u256` external + +Burns shares from `owner` and sends exactly `assets` of underlying tokens to `receiver`. + +Requirements: + +- `assets` is less than or equal to the max withdraw amount of `owner`. + +Emits a [Withdraw](#IERC4626-Withdraw) event. + +#### [](#ERC4626Component-max_redeem)`max_redeem(self: @ContractState, owner: ContractAddress) → u256` external + +Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault, through a [redeem](#ERC4626Component-redeem) call. + +The default max redeem value is the full balance of assets for `owner`. This can be changed in the implementing contract by defining custom logic in [LimitConfigTrait::redeem\_limit](#ERC4626Component-redeem_limit). + +With customized limits, the maximum redeem amount will either be the custom limit itself or `owner`'s total asset balance, whichever value is less. + +#### [](#ERC4626Component-preview_redeem)`preview_redeem(self: @ContractState, shares: u256) → u256` external + +Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block, given current on-chain conditions. + +The default redeem preview value is the full amount of assets. This can be changed to account for fees, for example, in the implementing contract by defining custom logic in [FeeConfigTrait::calculate\_redeem\_fee](#ERC4626Component-calculate_redeem_fee). + +This method must be inclusive of exit fees to be compliant with the [EIP-4626](https://eips.ethereum.org/EIPS/eip-4626) spec. + +#### [](#ERC4626Component-redeem)`redeem(self: @ContractState, shares: u256, receiver: ContractAddress, owner: ContractAddress) → u256` external + +Burns exactly `shares` from `owner` and sends assets of underlying tokens to `receiver`. + +Requirements: + +- `shares` is less than or equal to the max redeem amount of `owner`. + +Emits a [Withdraw](#IERC4626-Withdraw) event. + +#### [](#ERC4626Component-name)`name(self: @ContractState) → ByteArray` external + +Returns the name of the token. + +#### [](#ERC4626Component-symbol)`symbol(self: @ContractState) → ByteArray` external + +Returns the ticker symbol of the token, usually a shorter version of the name. + +#### [](#ERC4626Component-decimals)`decimals(self: @ContractState) → u8` external + +Returns the cumulative number of decimals which includes both `UNDERLYING_DECIMALS` and `OFFSET_DECIMALS`. Both of which must be defined in the [ImmutableConfig](#ERC4626Component-Immutable-Config) inside the implementing contract. + +#### [](#internal_functions)Internal functions + +#### [](#ERC4626Component-initializer)`initializer(ref self: ContractState, asset_address: ContractAddress)` internal + +Validates the [ImmutableConfig](#ERC4626Component-Immutable-Config) constants and sets the `asset_address` to the vault. This should be set in the contract’s constructor. + +Requirements: + +- `asset_address` cannot be the zero address. + +#### [](#ERC4626Component-_deposit)`_deposit(ref self: ContractState, caller: ContractAddress, receiver: ContractAddress, assets: u256, shares: u256)` internal + +Internal logic for [deposit](#ERC4626Component-deposit) and [mint](#ERC4626Component-mint). + +Transfers `assets` from `caller` to the Vault contract then mints `shares` to `receiver`. Fees can be transferred in the `ERC4626Hooks::after_deposit` hook which is executed after assets are transferred and shares are minted. + +Requirements: + +- [ERC20::transfer\_from](#ERC20Component-transfer_from) must return true. + +Emits two [ERC20::Transfer](#ERC20Component-Transfer) events (`ERC20::mint` and `ERC20::transfer_from`). + +Emits a [Deposit](#IERC4626-Deposit) event. + +#### [](#ERC4626Component-_withdraw)`_withdraw(ref self: ContractState, caller: ContractAddress, receiver: ContractAddress, owner: ContractAddress, assets: u256, shares: u256)` internal + +Internal logic for [withdraw](#ERC4626Component-withdraw) and [redeem](#ERC4626Component-redeem). + +Burns `shares` from `owner` and then transfers `assets` to `receiver`. Fees can be transferred in the `ERC4626Hooks::before_withdraw` hook which is executed before shares are burned and assets are transferred. + +Requirements: + +- [ERC20::transfer](#ERC20Component-transfer) must return true. + +Emits two [ERC20::Transfer](#ERC20Component-Transfer) events (`ERC20::burn` and `ERC20::transfer`). + +Emits a [Withdraw](#IERC4626-Withdraw) event. + +#### [](#ERC4626Component-_convert_to_shares)`_convert_to_shares(self: @ContractState, assets: u256, rounding: Rounding) -> u256` internal + +Internal conversion function (from assets to shares) with support for `rounding` direction. + +#### [](#ERC4626Component-_convert_to_assets)`_convert_to_assets(self: @ContractState, shares: u256, rounding: Rounding) -> u256` internal + +Internal conversion function (from shares to assets) with support for `rounding` direction. + +## [](#presets)Presets + +### [](#ERC20Upgradeable)`ERC20Upgradeable`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/presets/src/erc20.cairo) + +```cairo +use openzeppelin_presets::ERC20Upgradeable; +``` + +Upgradeable ERC20 contract leveraging [ERC20Component](#ERC20Component) with a fixed-supply mechanism for token distribution. + +[Sierra class hash](../presets) + +0x07802658d99373a4002434cbdc8897d1936c6b1beea48af0cc3b5574707f8d92 + +Constructor + +- [`constructor(self, name, symbol, fixed_supply, recipient, owner)`](#ERC20Upgradeable-constructor) + +Embedded Implementations + +ERC20MixinImpl + +- [`ERC20MixinImpl`](#ERC20Component-Embeddable-Mixin-Impl) + +OwnableMixinImpl + +- [`OwnableMixinImpl`](access#OwnableComponent-Mixin-Impl) + +External Functions + +- [`upgrade(self, new_class_hash)`](#ERC20Upgradeable-upgrade) + +#### [](#ERC20Upgradeable-constructor-section)Constructor + +#### [](#ERC20Upgradeable-constructor)`constructor(ref self: ContractState, name: ByteArray, symbol: ByteArray, fixed_supply: u256, recipient: ContractAddress, owner: ContractAddress)` constructor + +Sets the `name` and `symbol` and mints `fixed_supply` tokens to `recipient`. Assigns `owner` as the contract owner with permissions to upgrade. + +#### [](#ERC20Upgradeable-external-functions)External functions + +#### [](#ERC20Upgradeable-upgrade)`upgrade(ref self: ContractState, new_class_hash: ClassHash)` external + +Upgrades the contract to a new implementation given by `new_class_hash`. + +Requirements: + +- The caller is the contract owner. +- `new_class_hash` cannot be zero. diff --git a/content/cairo-contracts/api/erc721.mdx b/content/cairo-contracts/api/erc721.mdx new file mode 100644 index 00000000..0869c433 --- /dev/null +++ b/content/cairo-contracts/api/erc721.mdx @@ -0,0 +1,812 @@ +--- +title: ERC721 +--- + +This module provides interfaces, presets, and utilities related to ERC721 contracts. + +For an overview of ERC721, read our [ERC721 guide](../erc721). + +## [](#interfaces)Interfaces + +Starting from version `3.x.x`, the interfaces are no longer part of the `openzeppelin_token` package. The references documented here are contained in the `openzeppelin_interfaces` package version `v2.1.0`. + +### [](#IERC721)`IERC721`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/interfaces/src/token/erc721.cairo) + +```cairo +use openzeppelin_interfaces::erc721::IERC721; +``` + +Interface of the IERC721 standard as defined in [EIP721](https://eips.ethereum.org/EIPS/eip-721). + +[SRC5 ID](introspection#ISRC5) + +0x33eb2f84c309543403fd69f0d0f363781ef06ef6faeb0131ff16ea3175bd943 + +Functions + +- [`balance_of(account)`](#IERC721-balance_of) +- [`owner_of(token_id)`](#IERC721-owner_of) +- [`safe_transfer_from(from, to, token_id, data)`](#IERC721-safe_transfer_from) +- [`transfer_from(from, to, token_id)`](#IERC721-transfer_from) +- [`approve(to, token_id)`](#IERC721-approve) +- [`set_approval_for_all(operator, approved)`](#IERC721-set_approval_for_all) +- [`get_approved(token_id)`](#IERC721-get_approved) +- [`is_approved_for_all(owner, operator)`](#IERC721-is_approved_for_all) + +Events + +- [`Approval(owner, approved, token_id)`](#IERC721-Approval) +- [`ApprovalForAll(owner, operator, approved)`](#IERC721-ApprovalForAll) +- [`Transfer(from, to, token_id)`](#IERC721-Transfer) + +#### [](#functions)Functions + +#### [](#IERC721-balance_of)`balance_of(account: ContractAddress) → u256` external + +Returns the number of NFTs owned by `account`. + +#### [](#IERC721-owner_of)`owner_of(token_id: u256) → ContractAddress` external + +Returns the owner address of `token_id`. + +#### [](#IERC721-safe_transfer_from)`safe_transfer_from(from: ContractAddress, to: ContractAddress, token_id: u256, data: Span)` external + +Transfer ownership of `token_id` from `from` to `to`, checking first that `to` is aware of the ERC721 protocol to prevent tokens being locked forever. For information regarding how contracts communicate their awareness of the ERC721 protocol, see [Receiving Tokens](../erc721#receiving_tokens). + +Emits a [Transfer](#IERC721-Transfer) event. + +#### [](#IERC721-transfer_from)`transfer_from(from: ContractAddress, to: ContractAddress, token_id: u256)` external + +Transfer ownership of `token_id` from `from` to `to`. + +Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721 transfers or else they may be permanently lost. Usage of [IERC721::safe\_transfer\_from](#IERC721-safe_transfer_from) prevents loss, though the caller must understand this adds an external call which potentially creates a reentrancy vulnerability. + +Emits a [Transfer](#IERC721-Transfer) event. + +#### [](#IERC721-approve)`approve(to: ContractAddress, token_id: u256)` external + +Change or reaffirm the approved address for an NFT. + +Emits an [Approval](#IERC721-Approval) event. + +#### [](#IERC721-set_approval_for_all)`set_approval_for_all(operator: ContractAddress, approved: bool)` external + +Enable or disable approval for `operator` to manage all of the caller’s assets. + +Emits an [ApprovalForAll](#IERC721-ApprovalForAll) event. + +#### [](#IERC721-get_approved)`get_approved(token_id: u256) -> u256` external + +Returns the address approved for `token_id`. + +#### [](#IERC721-is_approved_for_all)`is_approved_for_all(owner: ContractAddress, operator: ContractAddress) -> bool` external + +Query if `operator` is an authorized operator for `owner`. + +#### [](#events)Events + +#### [](#IERC721-Approval)`Approval(owner: ContractAddress, approved: ContractAddress, token_id: u256)` event + +Emitted when `owner` enables `approved` to manage the `token_id` token. + +#### [](#IERC721-ApprovalForAll)`ApprovalForAll(owner: ContractAddress, operator: ContractAddress, approved: bool)` event + +Emitted when `owner` enables or disables `operator` to manage the `token_id` token. + +#### [](#IERC721-Transfer)`Transfer(from: ContractAddress, to: ContractAddress, token_id: u256)` event + +Emitted when `token_id` token is transferred from `from` to `to`. + +### [](#IERC721Metadata)`IERC721Metadata`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/interfaces/src/token/erc721.cairo) + +```cairo +use openzeppelin_interfaces::erc721::IERC721Metadata; +``` + +Interface for the optional metadata functions in [EIP721](https://eips.ethereum.org/EIPS/eip-721). + +[SRC5 ID](introspection#ISRC5) + +0xabbcd595a567dce909050a1038e055daccb3c42af06f0add544fa90ee91f25 + +Functions + +- [`name()`](#IERC721Metadata-name) +- [`symbol()`](#IERC721Metadata-symbol) +- [`token_uri(token_id)`](#IERC721Metadata-token_uri) + +#### [](#functions_2)Functions + +#### [](#IERC721Metadata-name)`name() -> ByteArray` external + +Returns the NFT name. + +#### [](#IERC721Metadata-symbol)`symbol() -> ByteArray` external + +Returns the NFT ticker symbol. + +#### [](#IERC721Metadata-token_uri)`token_uri(token_id: u256) -> ByteArray` external + +Returns the Uniform Resource Identifier (URI) for the `token_id` token. If the URI is not set for `token_id`, the return value will be an empty `ByteArray`. + +### [](#IERC721Receiver)`IERC721Receiver`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/interfaces/src/token/erc721.cairo) + +```cairo +use openzeppelin_interfaces::erc721::IERC721Receiver; +``` + +Interface for contracts that support receiving `safe_transfer_from` transfers. + +[SRC5 ID](introspection#ISRC5) + +0x3a0dff5f70d80458ad14ae37bb182a728e3c8cdda0402a5daa86620bdf910bc + +Functions + +- [`on_erc721_received(operator, from, token_id, data)`](#IERC721Receiver-on_erc721_received) + +#### [](#functions_3)Functions + +#### [](#IERC721Receiver-on_erc721_received)`on_erc721_received(operator: ContractAddress, from: ContractAddress, token_id: u256, data: Span) -> felt252` external + +Whenever an IERC721 `token_id` token is transferred to this non-account contract via [IERC721::safe\_transfer\_from](#IERC721-safe_transfer_from) by `operator` from `from`, this function is called. + +### [](#IERC721Enumerable)`IERC721Enumerable`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/interfaces/src/token/erc721.cairo) + +Interface for the optional enumerable functions in [EIP721](https://eips.ethereum.org/EIPS/eip-721). + +[SRC5 ID](introspection#ISRC5) + +0x16bc0f502eeaf65ce0b3acb5eea656e2f26979ce6750e8502a82f377e538c87 + +Functions + +- [`total_supply()`](#IERC721Enumerable-total_supply) +- [`token_by_index(index)`](#IERC721Enumerable-token_by_index) +- [`token_of_owner_by_index(owner, index)`](#IERC721Enumerable-token_of_owner_by_index) + +#### [](#functions_4)Functions + +#### [](#IERC721Enumerable-total_supply)`total_supply() -> u256` external + +Returns the total amount of tokens stored by the contract. + +#### [](#IERC721Enumerable-token_by_index)`token_by_index(index: u256) -> u256` external + +Returns a token id at a given `index` of all the tokens stored by the contract. Use along with [IERC721Enumerable::total\_supply](#IERC721Enumerable-total_supply) to enumerate all tokens. + +#### [](#IERC721Enumerable-token_of_owner_by_index)`token_of_owner_by_index(owner: ContractAddress, index: u256) -> u256` external + +Returns the token id owned by `owner` at a given `index` of its token list. Use along with [IERC721::balance\_of](#IERC721-balance_of) to enumerate all of `owner`'s tokens. + +## [](#core)Core + +### [](#ERC721Component)`ERC721Component`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/token/src/erc721/erc721.cairo) + +```cairo +use openzeppelin_token::erc721::ERC721Component; +``` + +ERC721 component implementing [IERC721](#IERC721) and [IERC721Metadata](#IERC721Metadata). + +Implementing [SRC5Component](introspection#SRC5Component) is a requirement for this component to be implemented. + +See [Hooks](#ERC721Component-Hooks) to understand how are hooks used. + +Hooks + +ERC721HooksTrait + +- [`before_update(self, to, token_id, auth)`](#ERC721Component-before_update) +- [`after_update(self, to, token_id, auth)`](#ERC721Component-after_update) + +[Embeddable Mixin Implementations](../components#mixins) + +ERC721MixinImpl + +- [`ERC721Impl`](#ERC721Component-Embeddable-Impls-ERC721Impl) +- [`ERC721MetadataImpl`](#ERC721Component-Embeddable-Impls-ERC721MetadataImpl) +- [`ERC721CamelOnlyImpl`](#ERC721Component-Embeddable-Impls-ERC721CamelOnlyImpl) +- [`ERC721MetadataCamelOnlyImpl`](#ERC721Component-Embeddable-Impls-ERC721MetadataCamelOnlyImpl) +- [`SRC5Impl`](introspection#SRC5Component-Embeddable-Impls) + +Embeddable Implementations + +ERC721Impl + +- [`balance_of(self, account)`](#ERC721Component-balance_of) +- [`owner_of(self, token_id)`](#ERC721Component-owner_of) +- [`safe_transfer_from(self, from, to, token_id, data)`](#ERC721Component-safe_transfer_from) +- [`transfer_from(self, from, to, token_id)`](#ERC721Component-transfer_from) +- [`approve(self, to, token_id)`](#ERC721Component-approve) +- [`set_approval_for_all(self, operator, approved)`](#ERC721Component-set_approval_for_all) +- [`get_approved(self, token_id)`](#ERC721Component-get_approved) +- [`is_approved_for_all(self, owner, operator)`](#ERC721Component-is_approved_for_all) + +ERC721MetadataImpl + +- [`name(self)`](#ERC721Component-name) +- [`symbol(self)`](#ERC721Component-symbol) +- [`token_uri(self, token_id)`](#ERC721Component-token_uri) + +ERC721CamelOnlyImpl + +- [`balanceOf(self, account)`](#ERC721Component-balanceOf) +- [`ownerOf(self, tokenId)`](#ERC721Component-ownerOf) +- [`safeTransferFrom(self, from, to, tokenId, data)`](#ERC721Component-safeTransferFrom) +- [`transferFrom(self, from, to, tokenId)`](#ERC721Component-transferFrom) +- [`setApprovalForAll(self, operator, approved)`](#ERC721Component-setApprovalForAll) +- [`getApproved(self, tokenId)`](#ERC721Component-getApproved) +- [`isApprovedForAll(self, owner, operator)`](#ERC721Component-isApprovedForAll) + +ERC721MetadataCamelOnlyImpl + +- [`tokenURI(self, tokenId)`](#ERC721Component-tokenURI) + +SRC5Impl + +- [`supports_interface(self, interface_id: felt252)`](introspection#ISRC5-supports_interface) + +Internal functions + +InternalImpl + +- [`initializer(self, name, symbol, base_uri)`](#ERC721Component-initializer) +- [`initializer_no_metadata(self)`](#ERC721Component-initializer_no_metadata) +- [`exists(self, token_id)`](#ERC721Component-exists) +- [`transfer(self, from, to, token_id)`](#ERC721Component-transfer) +- [`mint(self, to, token_id)`](#ERC721Component-mint) +- [`safe_transfer(self, from, to, token_id, data)`](#ERC721Component-safe_transfer) +- [`safe_mint(self, to, token_id, data)`](#ERC721Component-safe_mint) +- [`burn(self, token_id)`](#ERC721Component-burn) +- [`update(self, to, token_id, auth)`](#ERC721Component-update) +- [`_owner_of(self, token_id)`](#ERC721Component-_owner_of) +- [`_require_owned(self, token_id)`](#ERC721Component-_require_owned) +- [`_approve(self, to, token_id, auth)`](#ERC721Component-_approve) +- [`_approve_with_optional_event(self, to, token_id, auth, emit_event)`](#ERC721Component-_approve_with_optional_event) +- [`_set_approval_for_all(self, owner, operator, approved)`](#ERC721Component-_set_approval_for_all) +- [`_set_base_uri(self, base_uri)`](#ERC721Component-_set_base_uri) +- [`_base_uri(self)`](#ERC721Component-_base_uri) +- [`_is_authorized(self, owner, spender, token_id)`](#ERC721Component-_is_authorized) +- [`_check_authorized(self, owner, spender, token_id)`](#ERC721Component-_check_authorized) + +Events + +IERC721 + +- [`Approval(owner, approved, token_id)`](#ERC721Component-Approval) +- [`ApprovalForAll(owner, operator, approved)`](#ERC721Component-ApprovalForAll) +- [`Transfer(from, to, token_id)`](#ERC721Component-Transfer) + +#### [](#ERC721Component-Hooks)Hooks + +Hooks are functions which implementations can extend the functionality of the component source code. Every contract using ERC721Component is expected to provide an implementation of the ERC721HooksTrait. For basic token contracts, an empty implementation with no logic must be provided. + +You can use `openzeppelin_token::erc721::ERC721HooksEmptyImpl` which is already available as part of the library for this purpose. + +#### [](#ERC721Component-before_update)`before_update(ref self: ContractState, to: ContractAddress, token_id: u256, auth: ContractAddress)` hook + +Function executed at the beginning of the [update](#ERC721Component-update) function prior to any other logic. + +#### [](#ERC721Component-after_update)`after_update(ref self: ContractState, to: ContractAddress, token_id: u256, auth: ContractAddress)` hook + +Function executed at the end of the [update](#ERC721Component-update) function. + +#### [](#embeddable_functions)Embeddable functions + +#### [](#ERC721Component-balance_of)`balance_of(self: @ContractState, account: ContractAddress) → u256` external + +See [IERC721::balance\_of](#IERC721-balance_of). + +#### [](#ERC721Component-owner_of)`owner_of(self: @ContractState, token_id: u256) → ContractAddress` external + +See [IERC721::owner\_of](#IERC721-owner_of). + +Requirements: + +- `token_id` exists. + +#### [](#ERC721Component-safe_transfer_from)`safe_transfer_from(ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256, data: Span)` external + +See [IERC721::safe\_transfer\_from](#IERC721-safe_transfer_from). + +Requirements: + +- Caller is either approved or the `token_id` owner. +- `to` is not the zero address. +- `from` is not the zero address. +- `token_id` exists. +- `to` is either an account contract or supports the [IERC721Receiver](#IERC721Receiver) interface. + +#### [](#ERC721Component-transfer_from)`transfer_from(ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256)` external + +See [IERC721::transfer\_from](#IERC721-transfer_from). + +Requirements: + +- Caller either approved or the `token_id` owner. +- `to` is not the zero address. +- `from` is not the zero address. +- `token_id` exists. + +#### [](#ERC721Component-approve)`approve(ref self: ContractState, to: ContractAddress, token_id: u256)` external + +See [IERC721::approve](#IERC721-approve). + +Requirements: + +- The caller is either an approved operator or the `token_id` owner. +- `to` cannot be the token owner or the zero address. +- `token_id` exists. + +#### [](#ERC721Component-set_approval_for_all)`set_approval_for_all(ref self: ContractState, operator: ContractAddress, approved: bool)` external + +See [IERC721::set\_approval\_for\_all](#IERC721-set_approval_for_all). + +Requirements: + +- `operator` is not the zero address. + +#### [](#ERC721Component-get_approved)`get_approved(self: @ContractState, token_id: u256) -> u256` external + +See [IERC721::get\_approved](#IERC721-get_approved). + +Requirements: + +- `token_id` exists. + +#### [](#ERC721Component-is_approved_for_all)`is_approved_for_all(self: @ContractState, owner: ContractAddress, operator: ContractAddress) -> bool` external + +See [IERC721::is\_approved\_for\_all](#IERC721-is_approved_for_all). + +#### [](#ERC721Component-name)`name(self: @ContractState) -> ByteArray` external + +See [IERC721Metadata::name](#IERC721Metadata-name). + +#### [](#ERC721Component-symbol)`symbol(self: @ContractState) -> ByteArray` external + +See [IERC721Metadata::symbol](#IERC721Metadata-symbol). + +#### [](#ERC721Component-token_uri)`token_uri(self: @ContractState, token_id: u256) -> ByteArray` external + +Returns the Uniform Resource Identifier (URI) for the `token_id` token. If a base URI is set, the resulting URI for each token will be the concatenation of the base URI and the token ID. For example, the base URI `https://token-cdn-domain/` would be returned as `https://token-cdn-domain/123` for token ID `123`. + +If the URI is not set for `token_id`, the return value will be an empty `ByteArray`. + +#### [](#ERC721Component-balanceOf)`balanceOf(self: @ContractState, account: ContractAddress) -> u256` external + +See [ERC721Component::balance\_of](#ERC721Component-balance_of). + +#### [](#ERC721Component-ownerOf)`ownerOf(self: @ContractState, tokenId: u256) -> ContractAddress` external + +See [ERC721Component::owner\_of](#ERC721Component-owner_of). + +#### [](#ERC721Component-safeTransferFrom)`safeTransferFrom(ref self: ContractState, from: ContractAddress, to: ContractAddress, tokenId: u256, data: Span)` external + +See [ERC721Component::safe\_transfer\_from](#ERC721Component-safe_transfer_from). + +#### [](#ERC721Component-transferFrom)`transferFrom(ref self: ContractState, from: ContractAddress, to: ContractAddress, tokenId: u256)` external + +See [ERC721Component::transfer\_from](#ERC721Component-transfer_from). + +#### [](#ERC721Component-setApprovalForAll)`setApprovalForAll(ref self: ContractState, operator: ContractAddress, approved: bool)` external + +See [ERC721Component::set\_approval\_for\_all](#ERC721Component-set_approval_for_all). + +#### [](#ERC721Component-getApproved)`getApproved(self: @ContractState, tokenId: u256) -> ContractAddress` external + +See [ERC721Component::get\_approved](#ERC721Component-get_approved). + +#### [](#ERC721Component-isApprovedForAll)`isApprovedForAll(self: @ContractState, owner: ContractAddress, operator: ContractAddress) -> bool` external + +See [ERC721Component::is\_approved\_for\_all](#ERC721Component-is_approved_for_all). + +#### [](#ERC721Component-tokenURI)`tokenURI(self: @ContractState, tokenId: u256) -> ByteArray` external + +See [ERC721Component::token\_uri](#ERC721Component-token_uri). + +#### [](#internal_functions)Internal functions + +#### [](#ERC721Component-initializer)`initializer(ref self: ContractState, name: ByteArray, symbol: ByteArray, base_uri: ByteArray)` internal + +Initializes the contract by setting the token name and symbol. This should be used inside the contract’s constructor. + +Most ERC721 contracts expose the [IERC721Metadata](#IERC721Metadata) interface which is what this initializer is meant to support. If the contract DOES NOT expose the [IERC721Metadata](#IERC721Metadata) interface, meaning the token does not have a name, symbol, or URI, the contract must instead use [initializer\_no\_metadata](#ERC721Component-initializer_no_metadata) in the constructor. Failure to abide by these instructions can lead to unexpected issues especially with UIs. + +#### [](#ERC721Component-initializer_no_metadata)`initializer_no_metadata(ref self: ContractState)` internal + +Initializes the contract with no metadata by registering only the IERC721 interface. + +This initializer should ONLY be used during construction in the very specific instance when the contract does NOT expose the [IERC721Metadata](#IERC721Metadata) interface. Initializing a contract with this initializer means that tokens will not have a name, symbol, or URI. + +#### [](#ERC721Component-exists)`exists(self: @ContractState, token_id: u256) -> bool` internal + +Internal function that returns whether `token_id` exists. + +Tokens start existing when they are minted ([mint](#ERC721-mint)), and stop existing when they are burned ([burn](#ERC721-burn)). + +#### [](#ERC721Component-transfer)`transfer(ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256)` internal + +Transfers `token_id` from `from` to `to`. + +Internal function without access restriction. + +This method may lead to the loss of tokens if `to` is not aware of the ERC721 protocol. + +Requirements: + +- `to` is not the zero address. +- `from` is the token owner. +- `token_id` exists. + +Emits a [Transfer](#IERC721-Transfer) event. + +#### [](#ERC721Component-mint)`mint(ref self: ContractState, to: ContractAddress, token_id: u256)` internal + +Mints `token_id` and transfers it to `to`. Internal function without access restriction. + +This method may lead to the loss of tokens if `to` is not aware of the ERC721 protocol. + +Requirements: + +- `to` is not the zero address. +- `token_id` does not exist. + +Emits a [Transfer](#IERC721-Transfer) event. + +#### [](#ERC721Component-safe_transfer)`safe_transfer(ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256, data: Span)` internal + +Transfers ownership of `token_id` from `from` if `to` is either an account or `IERC721Receiver`. + +`data` is additional data, it has no specified format and is forwarded in `IERC721Receiver::on_erc721_received` to `to`. + +This method makes an external call to the recipient contract, which can lead to reentrancy vulnerabilities. + +Requirements: + +- `to` cannot be the zero address. +- `from` must be the token owner. +- `token_id` exists. +- `to` is either an account contract or supports the `IERC721Receiver` interface. + +Emits a [Transfer](#IERC721-Transfer) event. + +#### [](#ERC721Component-safe_mint)`safe_mint(ref self: ContractState, to: ContractAddress, token_id: u256, data: Span)` internal + +Mints `token_id` if `to` is either an account or `IERC721Receiver`. + +`data` is additional data, it has no specified format and is forwarded in `IERC721Receiver::on_erc721_received` to `to`. + +This method makes an external call to the recipient contract, which can lead to reentrancy vulnerabilities. + +Requirements: + +- `token_id` does not exist. +- `to` is either an account contract or supports the `IERC721Receiver` interface. + +Emits a [Transfer](#IERC721-Transfer) event. + +#### [](#ERC721Component-burn)`burn(ref self: ContractState, token_id: u256)` internal + +Destroys `token_id`. The approval is cleared when the token is burned. + +This internal function does not check if the caller is authorized to operate on the token. + +Requirements: + +- `token_id` exists. + +Emits a [Transfer](#IERC721-Transfer) event. + +#### [](#ERC721Component-update)`update(ref self: ContractState, to: ContractAddress, token_id: u256, auth: ContractAddress)` internal + +Transfers `token_id` from its current owner to `to`, or alternatively mints (or burns) if the current owner (or `to`) is the zero address. Returns the owner of the `token_id` before the update. + +The `auth` argument is optional. If the value passed is non-zero, then this function will check that `auth` is either the owner of the token, or approved to operate on the token (by the owner). + +Emits a [Transfer](#IERC721-Transfer) event. + +This function can be extended using the `ERC721HooksTrait`, to add functionality before and/or after the transfer, mint, or burn. + +#### [](#ERC721Component-_owner_of)`_owner_of(self: @ContractState, token_id: felt252) -> ContractAddress` internal + +Internal function that returns the owner address of `token_id`. + +#### [](#ERC721Component-_require_owned)`_require_owned(self: @ContractState, token_id: felt252) -> ContractAddress` internal + +Version of [\_owner\_of](#ERC721Component-_owner_of) that panics if owner is the zero address. + +#### [](#ERC721Component-_approve)`_approve(ref self: ContractState, to: ContractAddress, token_id: u256, auth: ContractAddress)` internal + +Approve `to` to operate on `token_id` + +The `auth` argument is optional. If the value passed is non-zero, then this function will check that `auth` is either the owner of the token, or approved to operate on all tokens held by this owner. + +Emits an [Approval](#IERC721-Approval) event. + +#### [](#ERC721Component-_approve_with_optional_event)`_approve_with_optional_event(ref self: ContractState, to: ContractAddress, token_id: u256, auth: ContractAddress, emit_event: bool)` internal + +Variant of [\_approve](#ERC721Component-_approve) with an optional flag to enable or disable the `Approval` event. The event is not emitted in the context of transfers. + +If `auth` is zero and `emit_event` is false, this function will not check that the token exists. + +Requirements: + +- if `auth` is non-zero, it must be either the owner of the token or approved to operate on all of its tokens. + +May emit an [Approval](#IERC721-Approval) event. + +#### [](#ERC721Component-_set_approval_for_all)`_set_approval_for_all(ref self: ContractState, owner: ContractAddress, operator: ContractAddress, approved: bool)` internal + +Enables or disables approval for `operator` to manage all of the `owner` assets. + +Requirements: + +- `operator` is not the zero address. + +Emits an [Approval](#IERC721-Approval) event. + +#### [](#ERC721Component-_set_base_uri)`_set_base_uri(ref self: ContractState, base_uri: ByteArray)` internal + +Internal function that sets the `base_uri`. + +#### [](#ERC721Component-_base_uri)`_base_uri(self: @ContractState) -> ByteArray` internal + +Base URI for computing [token\_uri](#IERC721Metadata-token_uri). + +If set, the resulting URI for each token will be the concatenation of the base URI and the token ID. Returns an empty `ByteArray` if not set. + +#### [](#ERC721Component-_is_authorized)`_is_authorized(self: @ContractState, owner: ContractAddress, spender: ContractAddress, token_id: u256) -> bool` internal + +Returns whether `spender` is allowed to manage `owner`'s tokens, or `token_id` in particular (ignoring whether it is owned by `owner`). + +This function assumes that `owner` is the actual owner of `token_id` and does not verify this assumption. + +#### [](#ERC721Component-_check_authorized)`_check_authorized(self: @ContractState, owner: ContractAddress, spender: ContractAddress, token_id: u256) -> bool` internal + +Checks if `spender` can operate on `token_id`, assuming the provided `owner` is the actual owner. + +Requirements: + +- `owner` cannot be the zero address. +- `spender` cannot be the zero address. +- `spender` must be the owner of `token_id` or be approved to operate on it. + +This function assumes that `owner` is the actual owner of `token_id` and does not verify this assumption. + +#### [](#events_2)Events + +#### [](#ERC721Component-Approval)`Approval(owner: ContractAddress, approved: ContractAddress, token_id: u256)` event + +See [IERC721::Approval](#IERC721-Approval). + +#### [](#ERC721Component-ApprovalForAll)`ApprovalForAll(owner: ContractAddress, operator: ContractAddress, approved: bool)` event + +See [IERC721::ApprovalForAll](#IERC721-ApprovalForAll). + +#### [](#ERC721Component-Transfer)`Transfer(from: ContractAddress, to: ContractAddress, token_id: u256)` event + +See [IERC721::Transfer](#IERC721-Transfer). + +### [](#ERC721ReceiverComponent)`ERC721ReceiverComponent`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/token/src/erc721/erc721_receiver.cairo) + +```cairo +use openzeppelin_token::erc721::ERC721ReceiverComponent; +``` + +ERC721Receiver component implementing [IERC721Receiver](#IERC721Receiver). + +Implementing [SRC5Component](introspection#SRC5Component) is a requirement for this component to be implemented. + +[Embeddable Mixin Implementations](../components#mixins) + +ERCReceiverMixinImpl + +- [`ERC721ReceiverImpl`](#ERC721ReceiverComponent-Embeddable-Impls-ERC721ReceiverImpl) +- [`ERC721ReceiverCamelImpl`](#ERC721ReceiverComponent-Embeddable-Impls-ERC721ReceiverCamelImpl) +- [`SRC5Impl`](introspection#SRC5Component-Embeddable-Impls) + +Embeddable Implementations + +ERC721ReceiverImpl + +- [`on_erc721_received(self, operator, from, token_id, data)`](#ERC721ReceiverComponent-on_erc721_received) + +ERC721ReceiverCamelImpl + +- [`onERC721Received(self, operator, from, tokenId, data)`](#ERC721ReceiverComponent-onERC721Received) + +Internal Functions + +InternalImpl + +- [`initializer(self)`](#ERC721ReceiverComponent-initializer) + +#### [](#embeddable_functions_2)Embeddable functions + +#### [](#ERC721ReceiverComponent-on_erc721_received)`on_erc721_received(self: @ContractState, operator: ContractAddress, from: ContractAddress, token_id: u256, data Span) -> felt252` external + +Returns the `IERC721Receiver` interface ID. + +#### [](#ERC721ReceiverComponent-onERC721Received)`onERC721Received(self: @ContractState, operator: ContractAddress, from: ContractAddress, token_id: u256, data Span) -> felt252` external + +See [ERC721ReceiverComponent::on\_erc721\_received](#ERC721ReceiverComponent-on_erc721_received). + +#### [](#internal_functions_2)Internal functions + +#### [](#ERC721ReceiverComponent-initializer)`initializer(ref self: ContractState)` internal + +Registers the `IERC721Receiver` interface ID as supported through introspection. + +## [](#extensions)Extensions + +### [](#ERC721EnumerableComponent)`ERC721EnumerableComponent`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/token/src/erc721/extensions/erc721_enumerable.cairo) + +```cairo +use openzeppelin_token::erc721::extensions::ERC721EnumerableComponent; +``` + +Extension of ERC721 as defined in the EIP that adds enumerability of all the token ids in the contract as well as all token ids owned by each account. This extension allows contracts to publish their entire list of NFTs and make them discoverable. + +Implementing [ERC721Component](#ERC721Component) is a requirement for this component to be implemented. + +To properly track token ids, this extension requires that the [ERC721EnumerableComponent::before\_update](#ERC721EnumerableComponent-before_update) function is called before every transfer, mint, or burn operation. For this, the [ERC721HooksTrait::before\_update](#ERC721Component-before_update) hook must be used. Here’s how the hook should be implemented in a contract: + +```[ +#[starknet::contract] +mod ERC721EnumerableContract { + (...) + + component!(path: ERC721Component, storage: erc721, event: ERC721Event); + component!(path: ERC721EnumerableComponent, storage: erc721_enumerable, event: ERC721EnumerableEvent); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + + impl ERC721HooksImpl of ERC721Component::ERC721HooksTrait { + fn before_update( + ref self: ERC721Component::ComponentState, + to: ContractAddress, + token_id: u256, + auth: ContractAddress + ) { + let mut contract_state = self.get_contract_mut(); + contract_state.erc721_enumerable.before_update(to, token_id); + } + } +} +``` + +Embeddable Implementations + +ERC721EnumerableImpl + +- [`total_supply(self)`](#ERC721EnumerableComponent-total_supply) +- [`token_by_index(self, index)`](#ERC721EnumerableComponent-token_by_index) +- [`token_of_owner_by_index(self, address, index)`](#ERC721EnumerableComponent-token_of_owner_by_index) + +Internal functions + +InternalImpl + +- [`initializer(self)`](#ERC721EnumerableComponent-initializer) +- [`before_update(self, to, token_id)`](#ERC721EnumerableComponent-before_update) +- [`all_tokens_of_owner(self, owner)`](#ERC721EnumerableComponent-all_tokens_of_owner) +- [`_add_token_to_owner_enumeration(self, to, token_id)`](#ERC721EnumerableComponent-_add_token_to_owner_enumeration) +- [`_add_token_to_all_tokens_enumeration(self, token_id)`](#ERC721EnumerableComponent-_add_token_to_all_tokens_enumeration) +- [`_remove_token_from_owner_enumeration(self, from, token_id)`](#ERC721EnumerableComponent-_remove_token_from_owner_enumeration) +- [`_remove_token_from_all_tokens_enumeration(self, token_id)`](#ERC721EnumerableComponent-_remove_token_from_all_tokens_enumeration) + +#### [](#ERC721EnumerableComponent-Embeddable-functions)Embeddable functions + +#### [](#ERC721EnumerableComponent-total_supply)`total_supply(self: @ContractState) → u256` external + +Returns the current amount of votes that `account` has. + +#### [](#ERC721EnumerableComponent-token_by_index)`token_by_index(self: @ContractState, index: u256) → u256` external + +See [IERC721Enumerable::token\_by\_index](#IERC721Enumerable-token_by_index). + +Requirements: + +- `index` is less than the total token supply. + +#### [](#ERC721EnumerableComponent-token_of_owner_by_index)`token_of_owner_by_index(self: @ContractState, owner: ContractAddress, index: u256) → u256` external + +See [IERC721Enumerable::token\_of\_owner\_by\_index](#IERC721Enumerable-token_of_owner_by_index). + +Requirements: + +- `index` is less than `owner`'s token balance. +- `owner` is not the zero address. + +#### [](#ERC721EnumerableComponent-Internal-functions)Internal functions + +#### [](#ERC721EnumerableComponent-initializer)`initializer(ref self: ContractState)` internal + +Registers the `IERC721Enumerable` interface ID as supported through introspection. + +#### [](#ERC721EnumerableComponent-before_update)`before_update(ref self: ContractState, to: ContractAddress, token_id: u256)` internal + +Updates the ownership and token-tracking data structures. + +When a token is minted (or burned), `token_id` is added to (or removed from) the token-tracking structures. + +When a token is transferred, minted, or burned, the ownership-tracking data structures reflect the change in ownership of `token_id`. + +This must be added to the implementing contract’s [ERC721HooksTrait::before\_update](#ERC721Component-before_update) hook. + +#### [](#ERC721EnumerableComponent-all_tokens_of_owner)`all_tokens_of_owner(self: @ContractState, owner: ContractAddress) → Span` internal + +Returns a list of all token ids owned by the specified `owner`. This function provides a more efficient alternative to calling `ERC721::balance_of` and iterating through tokens with `ERC721Enumerable::token_of_owner_by_index`. + +Requirements: + +- `owner` is not the zero address. + +#### [](#ERC721EnumerableComponent-_add_token_to_owner_enumeration)`_add_token_to_owner_enumeration(ref self: ContractState, to: ContractAddress, token_id: u256)` internal + +Adds token to this extension’s ownership-tracking data structures. + +#### [](#ERC721EnumerableComponent-_add_token_to_all_tokens_enumeration)`_add_token_to_all_tokens_enumeration(ref self: ContractState, token_id: u256)` internal + +Adds token to this extension’s token-tracking data structures. + +#### [](#ERC721EnumerableComponent-_remove_token_from_owner_enumeration)`_remove_token_from_owner_enumeration(ref self: ContractState, from: ContractAddress, token_id: u256)` internal + +Removes a token from this extension’s ownership-tracking data structures. + +This has 0(1) time complexity but alters the indexed order of owned tokens by swapping `token_id` and the index thereof with the last token id and the index thereof e.g. removing `1` from `[1, 2, 3, 4]` results in `[4, 2, 3]`. + +#### [](#ERC721EnumerableComponent-_remove_token_from_all_tokens_enumeration)`_remove_token_from_all_tokens_enumeration(ref self: ContractState, token_id: u256)` internal + +Removes `token_id` from this extension’s token-tracking data structures. + +This has 0(1) time complexity but alters the indexed order by swapping `token_id` and the index thereof with the last token id and the index thereof e.g. removing `1` from `[1, 2, 3, 4]` results in `[4, 2, 3]`. + +## [](#presets)Presets + +### [](#ERC721Upgradeable)`ERC721Upgradeable`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/presets/src/erc721.cairo) + +```cairo +use openzeppelin_presets::ERC721Upgradeable; +``` + +Upgradeable ERC721 contract leveraging [ERC721Component](#ERC721Component). + +[Sierra class hash](../presets) + +0x04080084ac1ba5a26b4638ac7ca2ff009a9a9b86bf6db5df05e96c90aa143df5 + +Constructor + +- [`constructor(self, name, symbol, recipient, token_ids, base_uri, owner)`](#ERC721Upgradeable-constructor) + +Embedded Implementations + +ERC721MixinImpl + +- [`ERC721MixinImpl`](#ERC721Component-Embeddable-Mixin-Impl) + +OwnableMixinImpl + +- [`OwnableMixinImpl`](access#OwnableComponent-Mixin-Impl) + +External Functions + +- [`upgrade(self, new_class_hash)`](#ERC721Upgradeable-upgrade) + +#### [](#ERC721Upgradeable-constructor-section)Constructor + +#### [](#ERC721Upgradeable-constructor)`constructor(ref self: ContractState, name: ByteArray, symbol: ByteArray, recipient: ContractAddress, token_ids: Span, base_uri: ByteArray, owner: ContractAddress)` constructor + +Sets the `name` and `symbol`. Mints `token_ids` tokens to `recipient` and sets the `base_uri`. Assigns `owner` as the contract owner with permissions to upgrade. + +#### [](#ERC721Upgradeable-external-functions)External functions + +#### [](#ERC721Upgradeable-upgrade)`upgrade(ref self: ContractState, new_class_hash: ClassHash)` external + +Upgrades the contract to a new implementation given by `new_class_hash`. + +Requirements: + +- The caller is the contract owner. +- `new_class_hash` cannot be zero. diff --git a/content/cairo-contracts/api/finance.mdx b/content/cairo-contracts/api/finance.mdx new file mode 100644 index 00000000..7942300a --- /dev/null +++ b/content/cairo-contracts/api/finance.mdx @@ -0,0 +1,224 @@ +--- +title: Finance +--- + +This crate includes primitives for financial systems. + +## [](#interfaces)Interfaces + +Starting from version `3.x.x`, the interfaces are no longer part of the `openzeppelin_finance` package. The references documented here are contained in the `openzeppelin_interfaces` package version `v2.1.0`. + +### [](#IVesting)`IVesting`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/interfaces/src/finance/vesting.cairo) + +```cairo +use openzeppelin_interfaces::vesting::IVesting; +``` + +Common interface for contracts implementing the vesting functionality. + +Functions + +- [`start()`](#IVesting-start) +- [`cliff()`](#IVesting-cliff) +- [`duration()`](#IVesting-duration) +- [`end()`](#IVesting-end) +- [`released(token)`](#IVesting-released) +- [`releasable(token)`](#IVesting-releasable) +- [`vested_amount(token, timestamp)`](#IVesting-vested_amount) +- [`release(token)`](#IVesting-release) + +Events + +- [`AmountReleased(token, amount)`](#IVesting-AmountReleased) + +#### [](#IVesting-Functions)Functions + +#### [](#IVesting-start)`start() → u64` external + +Returns the timestamp marking the beginning of the vesting period. + +#### [](#IVesting-cliff)`cliff() → u64` external + +Returns the timestamp marking the end of the cliff period. + +#### [](#IVesting-duration)`duration() → u64` external + +Returns the total duration of the vesting period. + +#### [](#IVesting-end)`end() → u64` external + +Returns the timestamp marking the end of the vesting period. + +#### [](#IVesting-released)`released(token: ContractAddress) → u256` external + +Returns the already released amount for a given `token`. + +#### [](#IVesting-releasable)`releasable(token: ContractAddress) → u256` external + +Returns the amount of a given `token` that can be released at the time of the call. + +#### [](#IVesting-vested_amount)`vested_amount(token: ContractAddress, timestamp: u64) → u256` external + +Returns the total vested amount of a specified `token` at a given `timestamp`. + +#### [](#IVesting-release)`release(token: ContractAddress) → u256` external + +Releases the amount of a given `token` that has already vested and returns that amount. + +May emit an [AmountReleased](#IVesting-AmountReleased) event. + +#### [](#IVesting-Events)Events + +#### [](#IVesting-AmountReleased)`AmountReleased(token: ContractAddress, amount: u256)` event + +Emitted when vested tokens are released to the beneficiary. + +## [](#vesting)Vesting + +### [](#VestingComponent)`VestingComponent`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/finance/src/vesting/vesting.cairo) + +```cairo +use openzeppelin_finance::vesting::VestingComponent; +``` + +Vesting component implementing the [`IVesting`](#IVesting) interface. + +Vesting Schedule Trait Implementations + +functions + +- [`calculate_vested_amount(self, token, total_allocation, timestamp, start, duration, cliff)`](#VestingComponent-calculate_vested_amount) + +Embeddable Implementations + +VestingImpl + +- [`start(self)`](#VestingComponent-start) +- [`cliff(self)`](#VestingComponent-cliff) +- [`duration(self)`](#VestingComponent-duration) +- [`end(self)`](#VestingComponent-end) +- [`released(self, token)`](#VestingComponent-released) +- [`releasable(self, token)`](#VestingComponent-releasable) +- [`vested_amount(self, token, timestamp)`](#VestingComponent-vested_amount) +- [`release(self, token)`](#VestingComponent-release) + +Internal implementations + +InternalImpl + +- [`initializer(self, start, duration, cliff_duration)`](#VestingComponent-initializer) +- [`resolve_vested_amount(self, token, timestamp)`](#VestingComponent-resolve_vested_amount) + +#### [](#VestingComponent-Vesting-Schedule)VestingSchedule trait + +A trait that defines the logic for calculating the vested amount based on a given timestamp. + +You can read more about the trait’s purpose and how to use it [here](../finance#vesting_schedule). + +#### [](#VestingComponent-calculate_vested_amount)`calculate_vested_amount(self: @ContractState, token: ContractAddress, total_allocation: u256, timestamp: u64, start: u64, duration: u64, cliff: u64) → u256` internal + +Calculates and returns the vested amount at a given `timestamp` based on the core vesting parameters. + +#### [](#VestingComponent-Functions)Functions + +#### [](#VestingComponent-start)`start(self: @ContractState) → u64` external + +Returns the timestamp marking the beginning of the vesting period. + +#### [](#VestingComponent-cliff)`cliff(self: @ContractState) → u64` external + +Returns the timestamp marking the end of the cliff period. + +#### [](#VestingComponent-duration)`duration(self: @ContractState) → u64` external + +Returns the total duration of the vesting period. + +#### [](#VestingComponent-end)`end(self: @ContractState) → u64` external + +Returns the timestamp marking the end of the vesting period. + +#### [](#VestingComponent-released)`released(self: @ContractState, token: ContractAddress) → u256` external + +Returns the already released amount for a given `token`. + +#### [](#VestingComponent-releasable)`releasable(self: @ContractState, token: ContractAddress) → u256` external + +Returns the amount of a given `token` that can be released at the time of the call. + +#### [](#VestingComponent-vested_amount)`vested_amount(self: @ContractState, token: ContractAddress, timestamp: u64) → u256` external + +Returns the total vested amount of a specified `token` at a given `timestamp`. + +#### [](#VestingComponent-release)`release(ref self: ContractState, token: ContractAddress) → u256` external + +Releases the amount of a given `token` that has already vested and returns that amount. + +If the releasable amount is zero, this function won’t emit the event or attempt to transfer the tokens. + +Requirements: + +- `transfer` call to the `token` must return `true` indicating a successful transfer. + +May emit an [AmountReleased](#IVesting-AmountReleased) event. + +#### [](#VestingComponent-Internal-Functions)Internal functions + +#### [](#VestingComponent-initializer)`initializer(ref self: ContractState, start: u64, duration: u64, cliff_duration: u64)` internal + +Initializes the component by setting the vesting `start`, `duration` and `cliff_duration`. To prevent reinitialization, this should only be used inside of a contract’s constructor. + +Requirements: + +- `cliff_duration` must be less than or equal to `duration`. + +#### [](#VestingComponent-resolve_vested_amount)`resolve_vested_amount(self: @ContractState, token: ContractAddress, timestamp: u64) → u256` internal + +Returns the vested amount that’s calculated using the [VestingSchedule](#VestingComponent-Vesting-Schedule) trait implementation. + +### [](#LinearVestingSchedule)`LinearVestingSchedule`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/finance/src/vesting/vesting.cairo) + +```cairo +use openzeppelin_finance::vesting::LinearVestingSchedule; +``` + +Defines the logic for calculating the vested amount, incorporating a cliff period. It returns 0 before the cliff ends. After the cliff period, the vested amount returned is directly proportional to the time passed since the start of the vesting schedule. + +## [](#presets)Presets + +### [](#VestingWallet)`VestingWallet`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/presets/src/vesting.cairo) + +```cairo +use openzeppelin::presets::VestingWallet; +``` + +A non-upgradable contract leveraging [VestingComponent](#VestingComponent) and [OwnableComponent](access#OwnableComponent). + +The contract is intentionally designed to be non-upgradable to ensure that neither the vesting initiator nor the vesting beneficiary can modify the vesting schedule without the consent of the other party. + +[Sierra class hash](../presets) + +0x062050f8eb6942d067d9d6fc6c2d01aaedbee284f339e58196d5a3bd3d4d6c6f + +Constructor + +- [`constructor(self, beneficiary, start, duration, cliff_duration)`](#VestingWallet-constructor) + +Embedded Implementations + +VestingComponent + +- [`VestingImpl`](#VestingComponent-Embeddable-Impls-VestingImpl) + +OwnableComponent + +- [`OwnableMixinImpl`](access#OwnableComponent-Mixin-Impl) + +#### [](#VestingWallet-constructor-section)Constructor + +#### [](#VestingWallet-constructor)`constructor(ref self: ContractState, beneficiary: ContractAddress, start: u64, duration: u64, cliff_duration: u64)` constructor + +Initializes the vesting component by setting the vesting `start`, `duration` and `cliff_duration`. Assigns `beneficiary` as the contract owner and the vesting beneficiary. + +Requirements: + +- `cliff_duration` must be less than or equal to `duration`. diff --git a/content/cairo-contracts/api/governance.mdx b/content/cairo-contracts/api/governance.mdx new file mode 100644 index 00000000..714f21fc --- /dev/null +++ b/content/cairo-contracts/api/governance.mdx @@ -0,0 +1,2769 @@ +--- +title: Governance +--- + +This crate includes primitives for on-chain governance. + +## [](#interfaces)Interfaces + +Starting from version `3.x.x`, the interfaces are no longer part of the `openzeppelin_governance` package. The references documented here are contained in the `openzeppelin_interfaces` package version `v2.1.0`. + +### [](#IGovernor)`IGovernor`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/interfaces/src/governance/governor.cairo) + +```cairo +use openzeppelin_interfaces::governor::IGovernor; +``` + +Interface of a governor contract. + +[SRC5 ID](introspection#ISRC5) + +0x1100a1f8546595b5bd75a6cd8fcc5b015370655e66f275963321c5cd0357ac9 + +Functions + +- [`name()`](#IGovernor-name) +- [`version()`](#IGovernor-version) +- [`COUNTING_MODE()`](#IGovernor-COUNTING_MODE) +- [`hash_proposal(calls, description_hash)`](#IGovernor-hash_proposal) +- [`state(proposal_id)`](#IGovernor-state) +- [`proposal_threshold()`](#IGovernor-proposal_threshold) +- [`proposal_snapshot(proposal_id)`](#IGovernor-proposal_snapshot) +- [`proposal_deadline(proposal_id)`](#IGovernor-proposal_deadline) +- [`proposal_proposer(proposal_id)`](#IGovernor-proposal_proposer) +- [`proposal_eta(proposal_id)`](#IGovernor-proposal_eta) +- [`proposal_needs_queuing(proposal_id)`](#IGovernor-proposal_needs_queuing) +- [`voting_delay()`](#IGovernor-voting_delay) +- [`voting_period()`](#IGovernor-voting_period) +- [`quorum(timepoint)`](#IGovernor-quorum) +- [`get_votes(account, timepoint)`](#IGovernor-get_votes) +- [`get_votes_with_params(account, timepoint, params)`](#IGovernor-get_votes_with_params) +- [`has_voted(proposal_id, account)`](#IGovernor-has_voted) +- [`propose(calls, description)`](#IGovernor-propose) +- [`queue(calls, description_hash)`](#IGovernor-queue) +- [`execute(calls, description_hash)`](#IGovernor-execute) +- [`cancel(proposal_id, description_hash)`](#IGovernor-cancel) +- [`cast_vote(proposal_id, support)`](#IGovernor-cast_vote) +- [`cast_vote_with_reason(proposal_id, support, reason)`](#IGovernor-cast_vote_with_reason) +- [`cast_vote_with_reason_and_params(proposal_id, support, reason, params)`](#IGovernor-cast_vote_with_reason_and_params) +- [`cast_vote_by_sig(proposal_id, support, reason, signature)`](#IGovernor-cast_vote_by_sig) +- [`cast_vote_with_reason_and_params_by_sig(proposal_id, support, reason, params, signature)`](#IGovernor-cast_vote_with_reason_and_params_by_sig) +- [`nonces(voter)`](#IGovernor-nonces) +- [`relay(call)`](#IGovernor-relay) + +Events + +- [`ProposalCreated(proposal_id, proposer, calls, signatures, vote_start, vote_end, description)`](#IGovernor-ProposalCreated) +- [`ProposalQueued(proposal_id, eta_seconds)`](#IGovernor-ProposalQueued) +- [`ProposalExecuted(proposal_id)`](#IGovernor-ProposalExecuted) +- [`ProposalCanceled(proposal_id)`](#IGovernor-ProposalCanceled) +- [`VoteCast(voter, proposal_id, support, weight, reason)`](#IGovernor-VoteCast) +- [`VoteCastWithParams(voter, proposal_id, support, weight, reason, params)`](#IGovernor-VoteCastWithParams) + +#### [](#IGovernor-Functions)Functions + +#### [](#IGovernor-name)`name() → felt252` external + +Name of the governor instance (used in building the [SNIP-12](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-12.md) domain separator). + +#### [](#IGovernor-version)`version() → felt252` external + +Version of the governor instance (used in building [SNIP-12](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-12.md) domain separator). + +#### [](#IGovernor-COUNTING_MODE)`COUNTING_MODE() → ByteArray` external + +A description of the possible `support` values for `cast_vote` and the way these votes are counted, meant to be consumed by UIs to show correct vote options and interpret the results. The string is a URL-encoded sequence of key-value pairs that each describe one aspect, for example `support=bravo&quorum=for,abstain`. + +There are 2 standard keys: `support` and `quorum`. + +- `support=bravo` refers to the vote options 0 = Against, 1 = For, 2 = Abstain, as in `GovernorBravo`. +- `quorum=bravo` means that only For votes are counted towards quorum. +- `quorum=for,abstain` means that both For and Abstain votes are counted towards quorum. + +If a counting module makes use of encoded `params`, it should include this under a `params` key with a unique name that describes the behavior. For example: + +- `params=fractional` might refer to a scheme where votes are divided fractionally between for/against/abstain. +- `params=erc721` might refer to a scheme where specific NFTs are delegated to vote. + +The string can be decoded by the standard [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams) JavaScript class. + +#### [](#IGovernor-hash_proposal)`hash_proposal(calls: Span, description_hash: felt252) → felt252` external + +Hashing function used to (re)build the proposal id from the proposal details. + +#### [](#IGovernor-state)`state(proposal_id: felt252) → ProposalState` external + +Returns the state of a proposal, given its id. + +#### [](#IGovernor-proposal_threshold)`proposal_threshold() → u256` external + +The number of votes required in order for a voter to become a proposer. + +#### [](#IGovernor-proposal_snapshot)`proposal_snapshot(proposal_id: felt252) → u64` external + +Timepoint used to retrieve user’s votes and quorum. If using block number, the snapshot is performed at the end of this block. Hence, voting for this proposal starts at the beginning of the following block. + +#### [](#IGovernor-proposal_deadline)`proposal_deadline(proposal_id: felt252) → u64` external + +Timepoint at which votes close. If using block number, votes close at the end of this block, so it is possible to cast a vote during this block. + +#### [](#IGovernor-proposal_proposer)`proposal_proposer(proposal_id: felt252) → ContractAddress` external + +The account that created a proposal. + +#### [](#IGovernor-proposal_eta)`proposal_eta(proposal_id: felt252) → u64` external + +The time when a queued proposal becomes executable ("ETA"). Unlike `proposal_snapshot` and `proposal_deadline`, this doesn’t use the governor clock, and instead relies on the executor’s clock which may be different. In most cases this will be a timestamp. + +#### [](#IGovernor-proposal_needs_queuing)`proposal_needs_queuing(proposal_id: felt252) → bool` external + +Whether a proposal needs to be queued before execution. This indicates if the proposal needs to go through a timelock. + +#### [](#IGovernor-voting_delay)`voting_delay() → u64` external + +Delay between when a proposal is created and when the vote starts. The unit this duration is expressed in depends on the clock (see [ERC-6372](https://eips.ethereum.org/EIPS/eip-6372)) this contract uses. + +This can be increased to leave time for users to buy voting power, or delegate it, before the voting of a proposal starts. + +#### [](#IGovernor-voting_period)`voting_period() → u64` external + +Delay between when a vote starts and when it ends. The unit this duration is expressed in depends on the clock (see [ERC-6372](https://eips.ethereum.org/EIPS/eip-6372)) this contract uses. + +The `voting_delay` can delay the start of the vote. This must be considered when setting the voting duration compared to the voting delay. + +This value is stored when the proposal is submitted so that possible changes to the value do not affect proposals that have already been submitted. + +#### [](#IGovernor-quorum)`quorum(timepoint: u64) → u256` external + +Minimum number of votes required for a proposal to be successful. + +The `timepoint` parameter corresponds to the snapshot used for counting vote. This allows the quorum to scale depending on values such as the total supply of a token at this timepoint. + +#### [](#IGovernor-get_votes)`get_votes(account: ContractAddress, timepoint: u64) → u256` external + +Returns the voting power of an `account` at a specific `timepoint`. + +This can be implemented in a number of ways, for example by reading the delegated balance from one (or multiple) `ERC20Votes` tokens. + +#### [](#IGovernor-get_votes_with_params)`get_votes_with_params(account: ContractAddress, timepoint: u64, params: Span) → u256` external + +Returns the voting power of an `account` at a specific `timepoint`, given additional encoded parameters. + +#### [](#IGovernor-has_voted)`has_voted(proposal_id: felt252, account: ContractAddress) → bool` external + +Returns whether an `account` has cast a vote on a proposal. + +#### [](#IGovernor-propose)`propose(calls: Span, description: ByteArray) → felt252` external + +Creates a new proposal. Vote starts after a delay specified by `voting_delay` and lasts for a duration specified by `voting_period`. + +The state of the Governor and targets may change between the proposal creation and its execution. This may be the result of third party actions on the targeted contracts, or other governor proposals. For example, the balance of this contract could be updated or its access control permissions may be modified, possibly compromising the proposal’s ability to execute successfully (e.g. the governor doesn’t have enough value to cover a proposal with multiple transfers). + +Returns the id of the proposal. + +#### [](#IGovernor-queue)`queue(calls: Span, description_hash: felt252) → felt252` external + +Queue a proposal. Some governors require this step to be performed before execution can happen. If queuing is not necessary, this function may revert. + +Queuing a proposal requires the quorum to be reached, the vote to be successful, and the deadline to be reached. + +Returns the id of the proposal. + +#### [](#IGovernor-execute)`execute(calls: span, description_hash: felt252) → felt252` external + +Execute a successful proposal. This requires the quorum to be reached, the vote to be successful, and the deadline to be reached. Depending on the governor it might also be required that the proposal was queued and that some delay passed. + +Some modules can modify the requirements for execution, for example by adding an additional timelock (See `timelock_controller`). + +Returns the id of the proposal. + +#### [](#IGovernor-cancel)`cancel(calls: Span, description_hash: felt252) → felt252` external + +Cancel a proposal. A proposal is cancellable by the proposer, but only while it is Pending state, i.e. before the vote starts. + +Returns the id of the proposal. + +#### [](#IGovernor-cast_vote)`cast_vote(proposal_id: felt252, support: u8) → u256` external + +Cast a vote on a proposal. + +Returns the weight of the vote. + +#### [](#IGovernor-cast_vote_with_reason)`cast_vote_with_reason(proposal_id: felt252, support: u8, reason: ByteArray) → u256` external + +Cast a vote on a proposal with a `reason`. + +Returns the weight of the vote. + +#### [](#IGovernor-cast_vote_with_reason_and_params)`cast_vote_with_reason_and_params(proposal_id: felt252, support: u8, reason: ByteArray, params: Span) → u256` external + +Cast a vote on a proposal with a reason and additional encoded parameters. + +Returns the weight of the vote. + +#### [](#IGovernor-cast_vote_by_sig)`cast_vote_by_sig(proposal_id: felt252, support: u8, voter: ContractAddress, signature: Span) → u256` external + +Cast a vote on a proposal using the voter’s signature. + +Returns the weight of the vote. + +#### [](#IGovernor-cast_vote_with_reason_and_params_by_sig)`cast_vote_with_reason_and_params_by_sig(proposal_id: felt252, support: u8, voter: ContractAddress, reason: ByteArray, params: Span, signature: Span) → u256` external + +Cast a vote on a proposal with a reason and additional encoded parameters using the `voter`'s signature. + +Returns the weight of the vote. + +#### [](#IGovernor-nonces)`nonces(voter: ContractAddress) → felt252` external + +Returns the next unused nonce for an address. + +#### [](#IGovernor-relay)`relay(call: Call)` external + +Relays a transaction or function call to an arbitrary target. + +In cases where the governance executor is some contract other than the governor itself, like when using a timelock, this function can be invoked in a governance proposal to recover tokens that were sent to the governor contract by mistake. + +If the executor is simply the governor itself, use of `relay` is redundant. + +#### [](#IGovernor-Events)Events + +#### [](#IGovernor-ProposalCreated)`ProposalCreated(proposal_id: felt252, proposer: ContractAddress, calls: Span, signatures: Span>, vote_start: u64, vote_end: u64, description: ByteArray)` event + +Emitted when a proposal is created. + +#### [](#IGovernor-ProposalQueued)`ProposalQueued(proposal_id: felt252, eta_seconds: u64)` event + +Emitted when a proposal is queued. + +#### [](#IGovernor-ProposalExecuted)`ProposalExecuted(proposal_id: felt252)` event + +Emitted when a proposal is executed. + +#### [](#IGovernor-ProposalCanceled)`ProposalCanceled(proposal_id: felt252)` event + +Emitted when a proposal is canceled. + +#### [](#IGovernor-VoteCast)`VoteCast(voter: ContractAddress, proposal_id: felt252, support: u8, weight: u256, reason: ByteArray)` event + +Emitted when a vote is cast. + +#### [](#IGovernor-VoteCastWithParams)`VoteCastWithParams(voter: ContractAddress, proposal_id: felt252, support: u8, weight: u256, reason: ByteArray, params: Span)` event + +Emitted when a vote is cast with params. + +### [](#IMultisig)`IMultisig`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/interfaces/src/governance/multisig.cairo) + +```cairo +use openzeppelin_interfaces::multisig::IMultisig; +``` + +Interface of a multisig contract. + +Functions + +- [`get_quorum()`](#IMultisig-get_quorum) +- [`is_signer(signer)`](#IMultisig-is_signer) +- [`get_signers()`](#IMultisig-get_signers) +- [`is_confirmed(id)`](#IMultisig-is_confirmed) +- [`is_confirmed_by(id, signer)`](#IMultisig-is_confirmed_by) +- [`is_executed(id)`](#IMultisig-is_executed) +- [`get_submitted_block(id)`](#IMultisig-get_submitted_block) +- [`get_transaction_state(id)`](#IMultisig-get_transaction_state) +- [`get_transaction_confirmations(id)`](#IMultisig-get_transaction_confirmations) +- [`hash_transaction(to, selector, calldata, salt)`](#IMultisig-hash_transaction) +- [`hash_transaction_batch(calls, salt)`](#IMultisig-hash_transaction_batch) +- [`add_signers(new_quorum, signers_to_add)`](#IMultisig-add_signers) +- [`remove_signers(new_quorum, signers_to_remove)`](#IMultisig-remove_signers) +- [`replace_signer(signer_to_remove, signer_to_add)`](#IMultisig-replace_signer) +- [`change_quorum(new_quorum)`](#IMultisig-change_quorum) +- [`submit_transaction(to, selector, calldata, salt)`](#IMultisig-submit_transaction) +- [`submit_transaction_batch(calls, salt)`](#IMultisig-submit_transaction_batch) +- [`confirm_transaction(id)`](#IMultisig-confirm_transaction) +- [`revoke_confirmation(id)`](#IMultisig-revoke_confirmation) +- [`execute_transaction(to, selector, calldata, salt)`](#IMultisig-execute_transaction) +- [`execute_transaction_batch(calls, salt)`](#IMultisig-execute_transaction_batch) + +Events + +- [`SignerAdded(signer)`](#IMultisig-SignerAdded) +- [`SignerRemoved(signer)`](#IMultisig-SignerRemoved) +- [`QuorumUpdated(old_quorum, new_quorum)`](#IMultisig-QuorumUpdated) +- [`TransactionSubmitted(id, signer)`](#IMultisig-TransactionSubmitted) +- [`TransactionConfirmed(id, signer)`](#IMultisig-TransactionConfirmed) +- [`ConfirmationRevoked(id, signer)`](#IMultisig-ConfirmationRevoked) +- [`TransactionExecuted(id)`](#IMultisig-TransactionExecuted) +- [`CallSalt(id, salt)`](#IMultisig-CallSalt) + +#### [](#IMultisig-Functions)Functions + +#### [](#IMultisig-get_quorum)`get_quorum() → u32` external + +Returns the current quorum value. The quorum is the minimum number of confirmations required to approve a transaction. + +#### [](#IMultisig-is_signer)`is_signer(signer: ContractAddress) → bool` external + +Returns whether the given `signer` is registered. Only registered signers can submit, confirm, or execute transactions. + +#### [](#IMultisig-get_signers)`get_signers() → Span` external + +Returns the list of all current signers. + +#### [](#IMultisig-is_confirmed)`is_confirmed(id: TransactionID) → bool` external + +Returns whether the transaction with the given `id` has been confirmed. + +#### [](#IMultisig-is_confirmed_by)`is_confirmed_by(id: TransactionID, signer: ContractAddress) → bool` external + +Returns whether the transaction with the given `id` has been confirmed by the specified `signer`. + +#### [](#IMultisig-is_executed)`is_executed(id: TransactionID) → bool` external + +Returns whether the transaction with the given `id` has been executed. + +#### [](#IMultisig-get_submitted_block)`get_submitted_block(id: TransactionID) → u64` external + +Returns the block number when the transaction with the given `id` was submitted. + +#### [](#IMultisig-get_transaction_state)`get_transaction_state(id: TransactionID) → TransactionState` external + +Returns the current state of the transaction with the given `id`. + +#### [](#IMultisig-get_transaction_confirmations)`get_transaction_confirmations(id: TransactionID) → u32` external + +Returns the number of confirmations from registered signers for the transaction with the specified `id`. + +#### [](#IMultisig-hash_transaction)`hash_transaction(to: ContractAddress, selector: felt252, calldata: Span, salt: felt252) → TransactionID` external + +Returns the computed identifier of a transaction containing a single call. + +#### [](#IMultisig-hash_transaction_batch)`hash_transaction_batch(calls: Span, salt: felt252) → TransactionID` external + +Returns the computed identifier of a transaction containing a batch of calls. + +#### [](#IMultisig-add_signers)`add_signers(new_quorum: u32, signers_to_add: Span)` external + +Adds new signers and updates the quorum. + +Requirements: + +- The caller must be the contract itself. +- `new_quorum` must be less than or equal to the total number of signers after addition. + +Emits a [SignerAdded](#IMultisig-SignerAdded) event for each signer added. + +Emits a [QuorumUpdated](#IMultisig-QuorumUpdated) event if the quorum changes. + +#### [](#IMultisig-remove_signers)`remove_signers(new_quorum: u32, signers_to_remove: Span)` external + +Removes signers and updates the quorum. + +Requirements: + +- The caller must be the contract itself. +- `new_quorum` must be less than or equal to the total number of signers after removal. + +Emits a [SignerRemoved](#IMultisig-SignerRemoved) event for each signer removed. + +Emits a [QuorumUpdated](#IMultisig-QuorumUpdated) event if the quorum changes. + +#### [](#IMultisig-replace_signer)`replace_signer(signer_to_remove: ContractAddress, signer_to_add: ContractAddress)` external + +Replaces an existing signer with a new signer. + +Requirements: + +- The caller must be the contract itself. +- `signer_to_remove` must be an existing signer. +- `signer_to_add` must not be an existing signer. + +Emits a [SignerRemoved](#IMultisig-SignerRemoved) event for the removed signer. + +Emits a [SignerAdded](#IMultisig-SignerAdded) event for the new signer. + +#### [](#IMultisig-change_quorum)`change_quorum(new_quorum: u32)` external + +Updates the quorum value to `new_quorum` if it differs from the current quorum. + +Requirements: + +- The caller must be the contract itself. +- `new_quorum` must be non-zero. +- `new_quorum` must be less than or equal to the total number of signers. + +Emits a [QuorumUpdated](#IMultisig-QuorumUpdated) event if the quorum changes. + +#### [](#IMultisig-submit_transaction)`submit_transaction(to: ContractAddress, selector: felt252, calldata: Span, salt: felt252) → TransactionID` external + +Submits a new transaction for confirmation. + +Requirements: + +- The caller must be a registered signer. +- The transaction must not have been submitted before. + +Emits a [TransactionSubmitted](#IMultisig-TransactionSubmitted) event. + +Emits a [CallSalt](#IMultisig-CallSalt) event if `salt` is not zero. + +#### [](#IMultisig-submit_transaction_batch)`submit_transaction_batch(calls: Span, salt: felt252) → TransactionID` external + +Submits a new batch transaction for confirmation. + +Requirements: + +- The caller must be a registered signer. +- The transaction must not have been submitted before. + +Emits a [TransactionSubmitted](#IMultisig-TransactionSubmitted) event. + +Emits a [CallSalt](#IMultisig-CallSalt) event if `salt` is not zero. + +#### [](#IMultisig-confirm_transaction)`confirm_transaction(id: TransactionID)` external + +Confirms a transaction with the given `id`. + +Requirements: + +- The caller must be a registered signer. +- The transaction must exist and not be executed. +- The caller must not have already confirmed the transaction. + +Emits a [TransactionConfirmed](#IMultisig-TransactionConfirmed) event. + +#### [](#IMultisig-revoke_confirmation)`revoke_confirmation(id: TransactionID)` external + +Revokes a previous confirmation for a transaction with the given `id`. + +Requirements: + +- The transaction must exist and not be executed. +- The caller must have previously confirmed the transaction. + +Emits a [ConfirmationRevoked](#IMultisig-ConfirmationRevoked) event. + +#### [](#IMultisig-execute_transaction)`execute_transaction(to: ContractAddress, selector: felt252, calldata: Span, salt: felt252)` external + +Executes a confirmed transaction. + +Requirements: + +- The caller must be a registered signer. +- The transaction must be confirmed and not yet executed. + +Emits a [TransactionExecuted](#IMultisig-TransactionExecuted) event. + +#### [](#IMultisig-execute_transaction_batch)`execute_transaction_batch(calls: Span, salt: felt252)` external + +Executes a confirmed batch transaction. + +Requirements: + +- The caller must be a registered signer. +- The transaction must be confirmed and not yet executed. + +Emits a [TransactionExecuted](#IMultisig-TransactionExecuted) event. + +#### [](#IMultisig-Events)Events + +#### [](#IMultisig-SignerAdded)`SignerAdded(signer: ContractAddress)` event + +Emitted when a new `signer` is added. + +#### [](#IMultisig-SignerRemoved)`SignerRemoved(signer: ContractAddress)` event + +Emitted when a `signer` is removed. + +#### [](#IMultisig-QuorumUpdated)`QuorumUpdated(old_quorum: u32, new_quorum: u32)` event + +Emitted when the `quorum` value is updated. + +#### [](#IMultisig-TransactionSubmitted)`TransactionSubmitted(id: TransactionID, signer: ContractAddress)` event + +Emitted when a new transaction is submitted by a `signer`. + +#### [](#IMultisig-TransactionConfirmed)`TransactionConfirmed(id: TransactionID, signer: ContractAddress)` event + +Emitted when a transaction is confirmed by a `signer`. + +#### [](#IMultisig-ConfirmationRevoked)`ConfirmationRevoked(id: TransactionID, signer: ContractAddress)` event + +Emitted when a `signer` revokes his confirmation. + +#### [](#IMultisig-TransactionExecuted)`TransactionExecuted(id: TransactionID)` event + +Emitted when a transaction is executed. + +#### [](#IMultisig-CallSalt)`CallSalt(id: felt252, salt: felt252)` event + +Emitted when a new transaction is submitted with non-zero salt. + +### [](#ITimelock)`ITimelock`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/interfaces/src/governance/timelock.cairo) + +```cairo +use openzeppelin_interfaces::timelock::ITimelock; +``` + +Interface of a timelock contract. + +Functions + +- [`is_operation(id)`](#ITimelock-is_operation) +- [`is_operation_pending(id)`](#ITimelock-is_operation_pending) +- [`is_operation_ready(id)`](#ITimelock-is_operation_ready) +- [`is_operation_done(id)`](#ITimelock-is_operation_done) +- [`get_timestamp(id)`](#ITimelock-get_timestamp) +- [`get_operation_state(id)`](#ITimelock-get_operation_state) +- [`get_min_delay()`](#ITimelock-get_min_delay) +- [`hash_operation(call, predecessor, salt)`](#ITimelock-hash_operation) +- [`hash_operation_batch(calls, predecessor, salt)`](#ITimelock-hash_operation_batch) +- [`schedule(call, predecessor, salt, delay)`](#ITimelock-schedule) +- [`schedule_batch(calls, predecessor, salt, delay)`](#ITimelock-schedule_batch) +- [`cancel(id)`](#ITimelock-cancel) +- [`execute(call, predecessor, salt)`](#ITimelock-execute) +- [`execute_batch(calls, predecessor, salt)`](#ITimelock-execute_batch) +- [`update_delay(new_delay)`](#ITimelock-update_delay) + +Events + +- [`CallScheduled(id, index, call, predecessor, delay)`](#ITimelock-CallScheduled) +- [`CallExecuted(id, index, call)`](#ITimelock-CallExecuted) +- [`CallSalt(id, salt)`](#ITimelock-CallSalt) +- [`CallCancelled(id)`](#ITimelock-CallCancelled) +- [`MinDelayChanged(old_duration, new_duration)`](#ITimelock-MinDelayChanged) + +#### [](#ITimelock-Functions)Functions + +#### [](#ITimelock-is_operation)`is_operation(id: felt252) → bool` external + +Returns whether `id` corresponds to a registered operation. This includes the OperationStates: `Waiting`, `Ready`, and `Done`. + +#### [](#ITimelock-is_operation_pending)`is_operation_pending(id: felt252) → bool` external + +Returns whether the `id` OperationState is pending or not. Note that a pending operation may be either `Waiting` or `Ready`. + +#### [](#ITimelock-is_operation_ready)`is_operation_ready(id: felt252) → bool` external + +Returns whether the `id` OperationState is `Ready` or not. + +#### [](#ITimelock-is_operation_done)`is_operation_done(id: felt252) → bool` external + +Returns whether the `id` OperationState is `Done` or not. + +#### [](#ITimelock-get_timestamp)`get_timestamp(id: felt252) → u64` external + +Returns the timestamp at which `id` becomes `Ready`. + +`0` means the OperationState is `Unset` and `1` means the OperationState is `Done`. + +#### [](#ITimelock-get_operation_state)`get_operation_state(id: felt252) → OperationState` external + +Returns the current state of the operation with the given `id`. + +The possible states are: + +- `Unset`: the operation has not been scheduled or has been canceled. +- `Waiting`: the operation has been scheduled and is pending the scheduled delay. +- `Ready`: the timer has expired, and the operation is eligible for execution. +- `Done`: the operation has been executed. + +#### [](#ITimelock-get_min_delay)`get_min_delay() → u64` external + +Returns the minimum delay in seconds for an operation to become valid. This value can be changed by executing an operation that calls `update_delay`. + +#### [](#ITimelock-hash_operation)`hash_operation(call: Call, predecessor: felt252, salt: felt252)` external + +Returns the identifier of an operation containing a single transaction. + +#### [](#ITimelock-hash_operation_batch)`hash_operation_batch(calls: Span, predecessor: felt252, salt: felt252)` external + +Returns the identifier of an operation containing a batch of transactions. + +#### [](#ITimelock-schedule)`schedule(call: Call, predecessor: felt252, salt: felt252, delay: u64)` external + +Schedule an operation containing a single transaction. + +Requirements: + +- The caller must have the `PROPOSER_ROLE` role. + +Emits [CallScheduled](#ITimelock-CallScheduled) event. Emits [CallSalt](#ITimelock-CallSalt) event if `salt` is not zero. + +#### [](#ITimelock-schedule_batch)`schedule_batch(calls: Span, predecessor: felt252, salt: felt252, delay: u64)` external + +Schedule an operation containing a batch of transactions. + +Requirements: + +- The caller must have the `PROPOSER_ROLE` role. + +Emits one [CallScheduled](#ITimelock-CallScheduled) event for each transaction in the batch. Emits [CallSalt](#ITimelock-CallSalt) event if `salt` is not zero. + +#### [](#ITimelock-cancel)`cancel(id: felt252)` external + +Cancels an operation. A canceled operation returns to `Unset` OperationState. + +Requirements: + +- The caller must have the `CANCELLER_ROLE` role. +- `id` must be a pending operation. + +Emits a [CallCancelled](#ITimelock-CallCancelled) event. + +#### [](#ITimelock-execute)`execute(call: Call, predecessor: felt252, salt: felt252)` external + +Execute a (Ready) operation containing a single Call. + +Requirements: + +- Caller must have `EXECUTOR_ROLE`. +- `id` must be in Ready OperationState. +- `predecessor` must either be `0` or in Done OperationState. + +Emits a [CallExecuted](#ITimelock-CallExecuted) event. + +This function can reenter, but it doesn’t pose a risk because [`_after_call(self: @ContractState, id: felt252)` internal](#TimelockControllerComponent-_after_call) checks that the proposal is pending, thus any modifications to the operation during reentrancy should be caught. + +#### [](#ITimelock-execute_batch)`execute_batch(calls: Span, predecessor: felt252, salt: felt252)` external + +Execute a (Ready) operation containing a batch of Calls. + +Requirements: + +- Caller must have `EXECUTOR_ROLE`. +- `id` must be in Ready OperationState. +- `predecessor` must either be `0` or in Done OperationState. + +Emits a [CallExecuted](#ITimelock-CallExecuted) event for each Call. + +This function can reenter, but it doesn’t pose a risk because `_after_call` checks that the proposal is pending, thus any modifications to the operation during reentrancy should be caught. + +#### [](#ITimelock-update_delay)`update_delay(new_delay: u64)` external + +Changes the minimum timelock duration for future operations. + +Requirements: + +- The caller must be the timelock itself. This can only be achieved by scheduling and later executing an operation where the timelock is the target and the data is the serialized call to this function. + +Emits a [MinDelayChanged](#ITimelock-MinDelayChanged) event. + +#### [](#ITimelock-Events)Events + +#### [](#ITimelock-CallScheduled)`CallScheduled(id: felt252, index: felt252, call: Call, predecessor: felt252, delay: u64)` event + +Emitted when `call` is scheduled as part of operation `id`. + +#### [](#ITimelock-CallExecuted)`CallExecuted(id: felt252, index: felt252, call: Call)` event + +Emitted when `call` is performed as part of operation `id`. + +#### [](#ITimelock-CallSalt)`CallSalt(id: felt252, salt: felt252)` event + +Emitted when a new proposal is scheduled with non-zero salt. + +#### [](#ITimelock-CallCancelled)`CallCancelled(id: felt252)` event + +Emitted when operation `id` is cancelled. + +#### [](#ITimelock-MinDelayChanged)`MinDelayChanged(old_duration: u64, new_duration: u64)` event + +Emitted when the minimum delay for future operations is modified. + +### [](#IVotes)`IVotes`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/interfaces/src/governance/votes.cairo) + +```cairo +use openzeppelin_interfaces::votes::IVotes; +``` + +Common interface for Votes-enabled contracts. + +Functions + +- [`get_votes(account)`](#IVotes-get_votes) +- [`get_past_votes(account, timepoint)`](#IVotes-get_past_votes) +- [`get_past_total_supply(timepoint)`](#IVotes-get_past_total_supply) +- [`delegates(account)`](#IVotes-delegates) +- [`delegate(delegatee)`](#IVotes-delegate) +- [`delegate_by_sig(delegator, delegatee, nonce, expiry, signature)`](#IVotes-delegate_by_sig) +- [`clock()`](#IVotes-clock) +- [`CLOCK_MODE()`](#IVotes-CLOCK_MODE) + +#### [](#IVotes-Functions)Functions + +#### [](#IVotes-get_votes)`get_votes(account: ContractAddress) → u256` external + +Returns the current amount of votes that `account` has. + +#### [](#IVotes-get_past_votes)`get_past_votes(account: ContractAddress, timepoint: u64) → u256` external + +Returns the amount of votes that `account` had at a specific moment in the past. + +#### [](#IVotes-get_past_total_supply)`get_past_total_supply(timepoint: u64) → u256` external + +Returns the total supply of votes available at a specific moment in the past. + +This value is the sum of all available votes, which is not necessarily the sum of all delegated votes. Votes that have not been delegated are still part of total supply, even though they would not participate in a vote. + +#### [](#IVotes-delegates)`delegates(account: ContractAddress) → ContractAddress` external + +Returns the delegate that `account` has chosen. + +#### [](#IVotes-delegate)`delegate(delegatee: ContractAddress)` external + +Delegates votes from the sender to `delegatee`. + +#### [](#IVotes-delegate_by_sig)`delegate_by_sig(delegator: ContractAddress, delegatee: ContractAddress, nonce: felt252, expiry: u64, signature: Span)` external + +Delegates votes from `delegator` to `delegatee` through a [SNIP-12](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-12.md) message signature validation. + +#### [](#IVotes-clock)`clock() → u64` external + +Returns the current timepoint determined by the contract’s operational mode, intended for use in time-sensitive logic. See [ERC-6372#clock](https://eips.ethereum.org/EIPS/eip-6372#clock). + +Requirements: + +- This function MUST always be non-decreasing. + +#### [](#IVotes-CLOCK_MODE)`CLOCK_MODE() → u64` external + +Returns a description of the clock the contract is operating in. See [ERC-6372#CLOCK\_MODE](https://eips.ethereum.org/EIPS/eip-6372#clock_mode). + +Requirements: + +- The output MUST be formatted like a URL query string, decodable in standard JavaScript. + +## [](#governor)Governor + +This modular system of Governor components allows the deployment of easily customizable on-chain voting protocols. + +For a walkthrough of how to implement a Governor, check the [Governor](../governance/governor) page. + +### [](#GovernorComponent)`GovernorComponent`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/governance/src/governor/governor.cairo) + +```cairo +use openzeppelin_governance::governor::GovernorComponent; +``` + +Core of the governance system. + +The extension traits presented below are what make the GovernorComponent a modular and configurable system. The embeddable and internal implementations depends on these trait. They can be implemented locally in the contract, or through the provided library [component extensions](#governor_extensions). + +Implementing [SRC5Component](introspection#SRC5Component) is a requirement for this component to be implemented. + +Extensions traits + +GovernorSettingsTrait + +- [`voting_delay(self)`](#GovernorComponent-GovernorSettingsTrait-voting_delay) +- [`voting_period(self)`](#GovernorComponent-GovernorSettingsTrait-voting_period) +- [`proposal_threshold(self)`](#GovernorComponent-GovernorSettingsTrait-proposal_threshold) + +GovernorQuorumTrait + +- [`quorum(self, timepoint)`](#GovernorComponent-GovernorQuorumTrait-quorum) + +GovernorCountingTrait + +- [`counting_mode(self)`](#GovernorComponent-GovernorCountingTrait-counting_mode) +- [`count_vote(self, proposal_id, account, support, total_weight, params)`](#GovernorComponent-GovernorCountingTrait-count_vote) +- [`has_voted(self, proposal_id, account)`](#GovernorComponent-GovernorCountingTrait-has_voted) +- [`quorum_reached(self, proposal_id)`](#GovernorComponent-GovernorCountingTrait-quorum_reached) +- [`vote_succeeded(self, proposal_id)`](#GovernorComponent-GovernorCountingTrait-vote_succeeded) + +GovernorVotesTrait + +- [`clock(self)`](#GovernorComponent-GovernorVotesTrait-clock) +- [`CLOCK_MODE(self)`](#GovernorComponent-GovernorVotesTrait-CLOCK_MODE) +- [`get_votes(self, account, timepoint, params)`](#GovernorComponent-GovernorVotesTrait-get_votes) + +GovernorExecutionTrait + +- [`state(self, proposal_id)`](#GovernorComponent-GovernorExecutionTrait-state) +- [`executor(self)`](#GovernorComponent-GovernorExecutionTrait-executor) +- [`execute_operations(self, proposal_id, calls, description_hash)`](#GovernorComponent-GovernorExecutionTrait-execute_operations) +- [`queue_operations(self, proposal_id, calls, description_hash)`](#GovernorComponent-GovernorExecutionTrait-queue_operations) +- [`proposal_needs_queuing(self, proposal_id)`](#GovernorComponent-GovernorExecutionTrait-proposal_needs_queuing) +- [`cancel_operations(self, proposal_id, description_hash)`](#GovernorComponent-GovernorExecutionTrait-cancel_operations) + +Embeddable Implementations + +GovernorImpl + +- [`name(self)`](#GovernorComponent-name) +- [`version(self)`](#GovernorComponent-version) +- [`COUNTING_MODE(self)`](#GovernorComponent-COUNTING_MODE) +- [`hash_proposal(self, calls, description_hash)`](#GovernorComponent-hash_proposal) +- [`state(self, proposal_id)`](#GovernorComponent-state) +- [`proposal_threshold(self)`](#GovernorComponent-proposal_threshold) +- [`proposal_snapshot(self, proposal_id)`](#GovernorComponent-proposal_snapshot) +- [`proposal_deadline(self, proposal_id)`](#GovernorComponent-proposal_deadline) +- [`proposal_proposer(self, proposal_id)`](#GovernorComponent-proposal_proposer) +- [`proposal_eta(self, proposal_id)`](#GovernorComponent-proposal_eta) +- [`proposal_needs_queuing(self, proposal_id)`](#GovernorComponent-proposal_needs_queuing) +- [`voting_delay(self)`](#GovernorComponent-voting_delay) +- [`voting_period(self)`](#GovernorComponent-voting_period) +- [`quorum(self, timepoint)`](#GovernorComponent-quorum) +- [`get_votes(self, account, timepoint)`](#GovernorComponent-get_votes) +- [`get_votes_with_params(self, account, timepoint, params)`](#GovernorComponent-get_votes_with_params) +- [`has_voted(self, proposal_id, account)`](#GovernorComponent-has_voted) +- [`propose(self, calls, description)`](#GovernorComponent-propose) +- [`queue(self, calls, description_hash)`](#GovernorComponent-queue) +- [`execute(self, calls, description_hash)`](#GovernorComponent-execute) +- [`cancel(self, proposal_id, description_hash)`](#GovernorComponent-cancel) +- [`cast_vote(self, proposal_id, support)`](#GovernorComponent-cast_vote) +- [`cast_vote_with_reason(self, proposal_id, support, reason)`](#GovernorComponent-cast_vote_with_reason) +- [`cast_vote_with_reason_and_params(self, proposal_id, support, reason, params)`](#GovernorComponent-cast_vote_with_reason_and_params) +- [`cast_vote_by_sig(self, proposal_id, support, reason, signature)`](#GovernorComponent-cast_vote_by_sig) +- [`cast_vote_with_reason_and_params_by_sig(self, proposal_id, support, reason, params, signature)`](#GovernorComponent-cast_vote_with_reason_and_params_by_sig) +- [`nonces(self, voter)`](#GovernorComponent-nonces) +- [`relay(self, call)`](#GovernorComponent-relay) + +Internal Implementations + +InternalImpl + +- [`initializer(self)`](#GovernorComponent-initializer) +- [`get_proposal(self, proposal_id)`](#GovernorComponent-get_proposal) +- [`is_valid_description_for_proposer(self, proposer, description)`](#GovernorComponent-is_valid_description_for_proposer) +- [`_hash_proposal(self, calls, description_hash)`](#GovernorComponent-_hash_proposal) +- [`_proposal_snapshot(self, proposal_id)`](#GovernorComponent-_proposal_snapshot) +- [`_proposal_deadline(self, proposal_id)`](#GovernorComponent-_proposal_deadline) +- [`_proposal_proposer(self, proposal_id)`](#GovernorComponent-_proposal_proposer) +- [`_proposal_eta(self, proposal_id)`](#GovernorComponent-_proposal_eta) + +InternalExtendedImpl + +- [`assert_only_governance(self)`](#GovernorComponent-assert_only_governance) +- [`validate_state(self, proposal_id, allowed_states)`](#GovernorComponent-validate_state) +- [`use_nonce(self, voter)`](#GovernorComponent-use_nonce) +- [`_get_votes(self, account, timepoint, params)`](#GovernorComponent-_get_votes) +- [`_proposal_threshold(self)`](#GovernorComponent-_proposal_threshold) +- [`_state(self, proposal_id)`](#GovernorComponent-_state) +- [`_propose(self, calls, description, proposer)`](#GovernorComponent-_propose) +- [`_cancel(self, proposal_id, description_hash)`](#GovernorComponent-_cancel) +- [`_count_vote(self, proposal_id, account, support, total_weight, params)`](#GovernorComponent-_count_vote) +- [`_cast_vote(self, proposal_id, voter, support, reason, params)`](#GovernorComponent-_cast_vote) + +Events + +- [`ProposalCreated(proposal_id, proposer, calls, signatures, vote_start, vote_end, description)`](#GovernorComponent-ProposalCreated) +- [`ProposalQueued(proposal_id)`](#GovernorComponent-ProposalQueued) +- [`ProposalExecuted(proposal_id)`](#GovernorComponent-ProposalExecuted) +- [`ProposalCanceled(proposal_id)`](#GovernorComponent-ProposalCanceled) +- [`VoteCast(voter, proposal_id, support, weight, reason)`](#GovernorComponent-VoteCast) +- [`VoteCastWithParams(voter, proposal_id, support, weight, reason, params)`](#GovernorComponent-VoteCastWithParams) + +#### [](#GovernorComponent-Extensions-Traits)Extensions traits functions + +#### [](#GovernorComponent-GovernorSettingsTrait-voting_delay)`voting_delay(self: @ContractState) → u64` extension + +Must return the delay, in number of timepoints, between when the proposal is created and when the vote starts. This can be increased to leave time for users to buy voting power, or delegate it, before the voting of a proposal starts. + +#### [](#GovernorComponent-GovernorSettingsTrait-voting_period)`voting_period(self: @ContractState) → u64` extension + +Must return the delay, in number of timepoints, between the vote start and vote end. + +#### [](#GovernorComponent-GovernorSettingsTrait-proposal_threshold)`proposal_threshold(self: @ContractState) → u256` extension + +Must return the minimum number of votes that an account must have to create a proposal. + +#### [](#GovernorComponent-GovernorQuorumTrait-quorum)`quorum(self: @ContractState, timepoint: u64) → u256` extension + +Must return the minimum number of votes required for a proposal to succeed. + +#### [](#GovernorComponent-GovernorCountingTrait-counting_mode)`counting_mode(self: @ContractState) → ByteArray` extension + +Must return a description of the possible `support` values for `cast_vote` and the way these votes are counted, meant to be consumed by UIs to show correct vote options and interpret the results. See [COUNTING\_MODE](#GovernorComponent-COUNTING_MODE) for more details. + +#### [](#GovernorComponent-GovernorCountingTrait-count_vote)`count_vote(ref self: ContractState, proposal_id: felt252, account: ContractAddress, support: u8, total_weight: u256, params: Span) → u256` extension + +Must register a vote for `proposal_id` by `account` with a given `support`, voting `weight` and voting `params`. + +Support is generic and can represent various things depending on the voting system used. + +#### [](#GovernorComponent-GovernorCountingTrait-has_voted)`has_voted(self: @ContractState, proposal_id: felt252, account: ContractAddress) → bool` extension + +Must return whether an account has cast a vote on a proposal. + +#### [](#GovernorComponent-GovernorCountingTrait-quorum_reached)`quorum_reached(self: @ContractState, proposal_id: felt252) → bool` extension + +Must return whether the minimum quorum has been reached for a proposal. + +#### [](#GovernorComponent-GovernorCountingTrait-vote_succeeded)`vote_succeeded(self: @ContractState, proposal_id: felt252) → bool` extension + +Must return whether a proposal has succeeded or not. + +#### [](#GovernorComponent-GovernorVotesTrait-clock)`clock(self: @ContractState) → u64` extension + +Returns the current timepoint determined by the governor’s operational mode, intended for use in time-sensitive logic. See [ERC-6372#clock](https://eips.ethereum.org/EIPS/eip-6372#clock). + +Requirements: + +- This function MUST always be non-decreasing. + +#### [](#GovernorComponent-GovernorVotesTrait-CLOCK_MODE)`CLOCK_MODE(self: @ContractState) → ByteArray` extension + +Returns a description of the clock the governor is operating in. See [ERC-6372#CLOCK\_MODE](https://eips.ethereum.org/EIPS/eip-6372#clock_mode). + +Requirements: + +- The output MUST be formatted like a URL query string, decodable in standard JavaScript. + +#### [](#GovernorComponent-GovernorVotesTrait-get_votes)`get_votes(self: @ContractState, account: ContractAddress, timepoint: u64, params: Span) → u256` extension + +Must return the voting power of an account at a specific timepoint with the given parameters. + +#### [](#GovernorComponent-GovernorExecutionTrait-state)`state(self: @ContractState, proposal_id: felt252) → ProposalState` extension + +Must return the state of a proposal at the current time. + +The state can be either: + +- `Pending`: The proposal does not exist yet. +- `Active`: The proposal is active. +- `Canceled`: The proposal has been canceled. +- `Defeated`: The proposal has been defeated. +- `Succeeded`: The proposal has succeeded. +- `Queued`: The proposal has been queued. +- `Executed`: The proposal has been executed. + +#### [](#GovernorComponent-GovernorExecutionTrait-executor)`executor(self: @ContractState) → ContractAddress` internal + +Must return the address through which the governor executes action. Should be used to specify whether the module execute actions through another contract such as a timelock. + +MUST be the governor itself, or an instance of TimelockController with the governor as the only proposer, canceller, and executor. + +When the executor is not the governor itself (i.e. a timelock), it can call functions that are restricted with the `assert_only_governance` guard, and also potentially execute transactions on behalf of the governor. Because of this, this module is designed to work with the TimelockController as the unique potential external executor. + +#### [](#GovernorComponent-GovernorExecutionTrait-execute_operations)`execute_operations(ref self: ContractState, proposal_id: felt252, calls: Span)` internal + +Execution mechanism. Can be used to modify the way operations are executed (for example adding a vault/timelock). + +#### [](#GovernorComponent-GovernorExecutionTrait-queue_operations)`queue_operations(ref self: ContractState, proposal_id: felt252, calls: Span)` internal + +Queuing mechanism. Can be used to modify the way queuing is performed (for example adding a vault/timelock). + +Requirements: + +- Must return a timestamp that describes the expected ETA for execution. If the returned value is 0, the core will consider queueing did not succeed, and the public `queue` function will revert. + +#### [](#GovernorComponent-GovernorExecutionTrait-proposal_needs_queuing)`proposal_needs_queuing(self: @ContractState) → bool` internal + +Must return whether proposals need to be queued before execution. This usually indicates if the proposal needs to go through a timelock. + +#### [](#GovernorComponent-GovernorExecutionTrait-cancel_operations)`cancel_operations(ref self: ContractState, proposal_id: felt252, calls: Span)` internal + +Cancel mechanism. Can be used to modify the way canceling is performed (for example adding a vault/timelock). + +#### [](#GovernorComponent-Embeddable-Functions)Embeddable functions + +#### [](#GovernorComponent-name)`name() → felt252` external + +Name of the governor instance (used in building the [SNIP-12](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-12.md) domain separator). + +#### [](#GovernorComponent-version)`version() → felt252` external + +Version of the governor instance (used in building [SNIP-12](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-12.md) domain separator). + +#### [](#GovernorComponent-COUNTING_MODE)`COUNTING_MODE() → ByteArray` external + +A description of the possible `support` values for `cast_vote` and the way these votes are counted, meant to be consumed by UIs to show correct vote options and interpret the results. The string is a URL-encoded sequence of key-value pairs that each describe one aspect, for example `support=bravo&quorum=for,abstain`. + +There are 2 standard keys: `support` and `quorum`. + +- `support=bravo` refers to the vote options 0 = Against, 1 = For, 2 = Abstain, as in `GovernorBravo`. +- `quorum=bravo` means that only For votes are counted towards quorum. +- `quorum=for,abstain` means that both For and Abstain votes are counted towards quorum. + +If a counting module makes use of encoded `params`, it should include this under a `params` key with a unique name that describes the behavior. For example: + +- `params=fractional` might refer to a scheme where votes are divided fractionally between for/against/abstain. +- `params=erc721` might refer to a scheme where specific NFTs are delegated to vote. + +The string can be decoded by the standard [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams) JavaScript class. + +#### [](#GovernorComponent-hash_proposal)`hash_proposal(calls: Span, description_hash: felt252) → felt252` external + +Hashing function used to (re)build the proposal id from the proposal details. + +#### [](#GovernorComponent-state)`state(proposal_id: felt252) → ProposalState` external + +Returns the state of a proposal, given its id. + +#### [](#GovernorComponent-proposal_threshold)`proposal_threshold() → u256` external + +The number of votes required in order for a voter to become a proposer. + +#### [](#GovernorComponent-proposal_snapshot)`proposal_snapshot(proposal_id: felt252) → u64` external + +Timepoint used to retrieve user’s votes and quorum. If using block number, the snapshot is performed at the end of this block. Hence, voting for this proposal starts at the beginning of the following block. + +#### [](#GovernorComponent-proposal_deadline)`proposal_deadline(proposal_id: felt252) → u64` external + +Timepoint at which votes close. If using block number, votes close at the end of this block, so it is possible to cast a vote during this block. + +#### [](#GovernorComponent-proposal_proposer)`proposal_proposer(proposal_id: felt252) → ContractAddress` external + +The account that created a proposal. + +#### [](#GovernorComponent-proposal_eta)`proposal_eta(proposal_id: felt252) → u64` external + +The time when a queued proposal becomes executable ("ETA"). Unlike `proposal_snapshot` and `proposal_deadline`, this doesn’t use the governor clock, and instead relies on the executor’s clock which may be different. In most cases this will be a timestamp. + +#### [](#GovernorComponent-proposal_needs_queuing)`proposal_needs_queuing(proposal_id: felt252) → bool` external + +Whether a proposal needs to be queued before execution. This indicates if the proposal needs to go through a timelock. + +#### [](#GovernorComponent-voting_delay)`voting_delay() → u64` external + +Delay between when a proposal is created and when the vote starts. The unit this duration is expressed in depends on the clock (see [ERC-6372](https://eips.ethereum.org/EIPS/eip-6372)) this contract uses. + +This can be increased to leave time for users to buy voting power, or delegate it, before the voting of a proposal starts. + +#### [](#GovernorComponent-voting_period)`voting_period() → u64` external + +Delay between the vote start and vote end. The unit this duration is expressed in depends on the clock (see [ERC-6372](https://eips.ethereum.org/EIPS/eip-6372)) this contract uses. + +The `voting_delay` can delay the start of the vote. This must be considered when setting the voting duration compared to the voting delay. + +This value is stored when the proposal is submitted so that possible changes to the value do not affect proposals that have already been submitted. + +#### [](#GovernorComponent-quorum)`quorum(timepoint: u64) → u256` external + +Minimum number of votes required for a proposal to be successful. + +The `timepoint` parameter corresponds to the snapshot used for counting vote. This allows the quorum to scale depending on values such as the total supply of a token at this timepoint. + +#### [](#GovernorComponent-get_votes)`get_votes(account: ContractAddress, timepoint: u64) → u256` external + +Returns the voting power of an `account` at a specific `timepoint`. + +This can be implemented in a number of ways, for example by reading the delegated balance from one (or multiple) `ERC20Votes` tokens. + +#### [](#GovernorComponent-get_votes_with_params)`get_votes_with_params(account: ContractAddress, timepoint: u64, params: Span) → u256` external + +Returns the voting power of an account at a specific timepoint, given additional encoded parameters. + +#### [](#GovernorComponent-has_voted)`has_voted(proposal_id: felt252, account: ContractAddress) → bool` external + +Returns whether an account has cast a vote on a proposal. + +#### [](#GovernorComponent-propose)`propose(calls: Span, description: ByteArray) → felt252` external + +Creates a new proposal. Voting starts after the delay specified by `voting_delay` and lasts for a duration specified by `voting_period`. Returns the id of the proposal. + +This function has opt-in frontrunning protection, described in `is_valid_description_for_proposer`. + +The state of the Governor and targets may change between the proposal creation and its execution. This may be the result of third party actions on the targeted contracts, or other governor proposals. For example, the balance of this contract could be updated or its access control permissions may be modified, possibly compromising the proposal’s ability to execute successfully (e.g. the governor doesn’t have enough value to cover a proposal with multiple transfers). + +Requirements: + +- The proposer must be authorized to submit the proposal. +- The proposer must have enough votes to submit the proposal if `proposal_threshold` is greater than zero. +- The proposal must not already exist. + +Emits a [ProposalCreated](#GovernorComponent-ProposalCreated) event. + +#### [](#GovernorComponent-queue)`queue(calls: Span, description_hash: felt252) → felt252` external + +Queues a proposal. Some governors require this step to be performed before execution can happen. If queuing is not necessary, this function may revert. Queuing a proposal requires the quorum to be reached, the vote to be successful, and the deadline to be reached. + +Returns the id of the proposal. + +Requirements: + +- The proposal must be in the `Succeeded` state. +- The queue operation must return a non-zero ETA. + +Emits a [ProposalQueued](#GovernorComponent-ProposalQueued) event. + +#### [](#GovernorComponent-execute)`execute(calls: span, description_hash: felt252) → felt252` external + +Executes a successful proposal. This requires the quorum to be reached, the vote to be successful, and the deadline to be reached. Depending on the governor it might also be required that the proposal was queued and that some delay passed. + +Some modules can modify the requirements for execution, for example by adding an additional timelock (See `timelock_controller`). + +Returns the id of the proposal. + +Requirements: + +- The proposal must be in the `Succeeded` or `Queued` state. + +Emits a [ProposalExecuted](#GovernorComponent-ProposalExecuted) event. + +#### [](#GovernorComponent-cancel)`cancel(calls: Span, description_hash: felt252) → felt252` external + +Cancels a proposal. A proposal is cancellable by the proposer, but only while it is Pending state, i.e. before the vote starts. + +Returns the id of the proposal. + +Requirements: + +- The proposal must be in the `Pending` state. +- The caller must be the proposer of the proposal. + +Emits a [ProposalCanceled](#GovernorComponent-ProposalCanceled) event. + +#### [](#GovernorComponent-cast_vote)`cast_vote(proposal_id: felt252, support: u8) → u256` external + +Cast a vote. + +Requirements: + +- The proposal must be active. + +Emits a [VoteCast](#GovernorComponent-VoteCast) event. + +#### [](#GovernorComponent-cast_vote_with_reason)`cast_vote_with_reason(proposal_id: felt252, support: u8, reason: ByteArray) → u256` external + +Cast a vote with a `reason`. + +Requirements: + +- The proposal must be active. + +Emits a [VoteCast](#GovernorComponent-VoteCast) event. + +#### [](#GovernorComponent-cast_vote_with_reason_and_params)`cast_vote_with_reason_and_params(proposal_id: felt252, support: u8, reason: ByteArray, params: Span) → u256` external + +Cast a vote with a `reason` and additional serialized `params`. + +Requirements: + +- The proposal must be active. + +Emits either: + +- [VoteCast](#GovernorComponent-VoteCast) event if no params are provided. +- [VoteCastWithParams](#GovernorComponent-VoteCastWithParams) event otherwise. + +#### [](#GovernorComponent-cast_vote_by_sig)`cast_vote_by_sig(proposal_id: felt252, support: u8, voter: ContractAddress, signature: Span) → u256` external + +Cast a vote using the `voter`'s signature. + +Requirements: + +- The proposal must be active. +- The nonce in the signed message must match the account’s current nonce. +- `voter` must implement `SRC6::is_valid_signature`. +- `signature` must be valid for the message hash. + +Emits a [VoteCast](#GovernorComponent-VoteCast) event. + +#### [](#GovernorComponent-cast_vote_with_reason_and_params_by_sig)`cast_vote_with_reason_and_params_by_sig(proposal_id: felt252, support: u8, voter: ContractAddress, reason: ByteArray, params: Span, signature: Span) → u256` external + +Cast a vote with a `reason` and additional serialized `params` using the `voter`'s signature. + +Requirements: + +- The proposal must be active. +- The nonce in the signed message must match the account’s current nonce. +- `voter` must implement `SRC6::is_valid_signature`. +- `signature` must be valid for the message hash. + +Emits either: + +- [VoteCast](#GovernorComponent-VoteCast) event if no params are provided. +- [VoteCastWithParams](#GovernorComponent-VoteCastWithParams) event otherwise. + +#### [](#GovernorComponent-nonces)`nonces(voter: ContractAddress) → felt252` external + +Returns the next unused nonce for an address. + +#### [](#GovernorComponent-relay)`relay(call: Call)` external + +Relays a transaction or function call to an arbitrary target. + +In cases where the governance executor is some contract other than the governor itself, like when using a timelock, this function can be invoked in a governance proposal to recover tokens that were sent to the governor contract by mistake. + +If the executor is simply the governor itself, use of `relay` is redundant. + +#### [](#GovernorComponent-Internal-Functions)Internal functions + +#### [](#GovernorComponent-initializer)`initializer(ref self: ContractState)` internal + +Initializes the contract by registering the supported interface id. + +#### [](#GovernorComponent-get_proposal)`get_proposal(self: @ContractState, proposal_id: felt252) → ProposalCore` internal + +Returns the proposal object given its id. + +#### [](#GovernorComponent-is_valid_description_for_proposer)`is_valid_description_for_proposer(self: @ContractState, proposer: ContractAddress, description: ByteArray) → bool` internal + +Checks if the proposer is authorized to submit a proposal with the given description. + +If the proposal description ends with `#proposer=0x???`, where `0x???` is an address written as a hex string (case insensitive), then the submission of this proposal will only be authorized to said address. + +This is used for frontrunning protection. By adding this pattern at the end of their proposal, one can ensure that no other address can submit the same proposal. An attacker would have to either remove or change that part, which would result in a different proposal id. + +In Starknet, the Sequencer ensures the order of transactions, but frontrunning can still be achieved by nodes, and potentially other actors in the future with sequencer decentralization. + +If the description does not match this pattern, it is unrestricted and anyone can submit it. This includes: + +- If the `0x???` part is not a valid hex string. +- If the `0x???` part is a valid hex string, but does not contain exactly 64 hex digits. +- If it ends with the expected suffix followed by newlines or other whitespace. +- If it ends with some other similar suffix, e.g. `#other=abc`. +- If it does not end with any such suffix. + +#### [](#GovernorComponent-_hash_proposal)`_hash_proposal(self: @ContractState, calls: Span, description_hash: felt252) → felt252` internal + +Returns the proposal id computed from the given parameters. + +The proposal id is computed as a Pedersen hash of: + +- The array of calls being proposed +- The description hash + +#### [](#GovernorComponent-_proposal_snapshot)`_proposal_snapshot(self: @ContractState, proposal_id: felt252) → u64` internal + +Timepoint used to retrieve user’s votes and quorum. If using block number, the snapshot is performed at the end of this block. Hence, voting for this proposal starts at the beginning of the following block. + +#### [](#GovernorComponent-_proposal_deadline)`_proposal_deadline(self: @ContractState, proposal_id: felt252) → u64` internal + +Timepoint at which votes close. If using block number, votes close at the end of this block, so it is possible to cast a vote during this block. + +#### [](#GovernorComponent-_proposal_proposer)`_proposal_proposer(self: @ContractState, proposal_id: felt252) → ContractAddress` internal + +The account that created a proposal. + +#### [](#GovernorComponent-_proposal_eta)`_proposal_eta(self: @ContractState, proposal_id: felt252) → u64` internal + +The time when a queued proposal becomes executable ("ETA"). Unlike `proposal_snapshot` and `proposal_deadline`, this doesn’t use the governor clock, and instead relies on the executor’s clock which may be different. In most cases this will be a timestamp. + +#### [](#GovernorComponent-assert_only_governance)`assert_only_governance(self: @ContractState)` internal + +Asserts that the caller is the governance executor. + +When the executor is not the governor itself (i.e. a timelock), it can call functions that are restricted with this modifier, and also potentially execute transactions on behalf of the governor. Because of this, this module is designed to work with the TimelockController as the unique potential external executor. The timelock MUST have the governor as the only proposer, canceller, and executor. + +#### [](#GovernorComponent-validate_state)`validate_state(self: @ContractState, proposal_id: felt252, state: ProposalState)` internal + +Validates that a proposal is in the expected state. Otherwise it panics. + +#### [](#GovernorComponent-use_nonce)`use_nonce(ref self: ContractState) → felt252` internal + +Consumes a nonce, returns the current value, and increments nonce. + +#### [](#GovernorComponent-_get_votes)`_get_votes(self: @ContractState, account: ContractAddress, timepoint: u64, params: Span) → u256` internal + +Internal wrapper for `GovernorVotesTrait::get_votes`. + +#### [](#GovernorComponent-_proposal_threshold)`_proposal_threshold(self: @ContractState) → u256` internal + +Internal wrapper for `GovernorProposeTrait::proposal_threshold`. + +#### [](#GovernorComponent-_state)`_state(self: @ContractState, proposal_id: felt252) → ProposalState` internal + +Returns the state of a proposal, given its id. + +Requirements: + +- The proposal must exist. + +#### [](#GovernorComponent-_propose)`_propose(ref self: ContractState, calls: Span, description_hash: felt252) → felt252` internal + +Internal propose mechanism. Returns the proposal id. + +Requirements: + +- The proposal must not already exist. + +Emits a [ProposalCreated](#GovernorComponent-ProposalCreated) event. + +#### [](#GovernorComponent-_cancel)`_cancel(ref self: ContractState, proposal_id: felt252)` internal + +Internal cancel mechanism with minimal restrictions. + +A proposal can be cancelled in any state other than Canceled or Executed. + +Once cancelled, a proposal can’t be re-submitted. + +#### [](#GovernorComponent-_count_vote)`_count_vote(ref self: ContractState, proposal_id: felt252, account: ContractAddress, support: u8, weight: u256, params: Span)` internal + +Internal wrapper for `GovernorCountingTrait::count_vote`. + +#### [](#GovernorComponent-_cast_vote)`_cast_vote(ref self: ContractState, proposal_id: felt252, account: ContractAddress, support: u8, reason: ByteArray, params: Span) → u256` internal + +Internal vote-casting mechanism. + +Checks that the vote is pending and that it has not been cast yet. This function retrieves the voting weight using `get_votes` and then calls the `_count_vote` internal function. + +Emits either: + +- [VoteCast](#GovernorComponent-VoteCast) event if no params are provided. +- [VoteCastWithParams](#GovernorComponent-VoteCastWithParams) event otherwise. + +#### [](#GovernorComponent-Events)Events + +#### [](#GovernorComponent-ProposalCreated)`ProposalCreated(proposal_id: felt252, proposer: ContractAddress, calls: Span, signatures: Span>, vote_start: u64, vote_end: u64, description: ByteArray)` event + +Emitted when a proposal is created. + +#### [](#GovernorComponent-ProposalQueued)`ProposalQueued(proposal_id: felt252, eta_seconds: u64)` event + +Emitted when a proposal is queued. + +#### [](#GovernorComponent-ProposalExecuted)`ProposalExecuted(proposal_id: felt252)` event + +Emitted when a proposal is executed. + +#### [](#GovernorComponent-ProposalCanceled)`ProposalCanceled(proposal_id: felt252)` event + +Emitted when a proposal is canceled. + +#### [](#GovernorComponent-VoteCast)`VoteCast(voter: ContractAddress, proposal_id: felt252, support: u8, weight: u256, reason: ByteArray)` event + +Emitted when a vote is cast. + +#### [](#GovernorComponent-VoteCastWithParams)`VoteCastWithParams(voter: ContractAddress, proposal_id: felt252, support: u8, weight: u256, reason: ByteArray, params: Span)` event + +Emitted when a vote is cast with params. + +## [](#governor_extensions)Governor extensions + +The Governor component can (and must) be extended by implementing the [extensions traits](#GovernorComponent-Extensions-Traits-Traits) to add the desired functionality. This can be achieved by directly implementing the traits on your contract, or by using a set of ready-to-use extensions provided by the library, which are presented below. + +### [](#GovernorCoreExecutionComponent)`GovernorCoreExecutionComponent`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/governance/src/governor/extensions/governor_core_execution.cairo) + +```cairo +use openzeppelin_governance::governor::extensions::GovernorCoreExecutionComponent; +``` + +Extension of [GovernorComponent](#GovernorComponent) providing an execution mechanism directly through the Governor itself. For a timelocked execution mechanism, see [GovernorTimelockExecutionComponent](#GovernorTimelockExecutionComponent). + +Extension traits implementations + +GovernorExecution + +- [`state(self, proposal_id)`](#GovernorCoreExecutionComponent-state) +- [`executor(self)`](#GovernorCoreExecutionComponent-executor) +- [`execute_operations(self, proposal_id, calls, description_hash)`](#GovernorCoreExecutionComponent-execute_operations) +- [`queue_operations(self, proposal_id, calls, description_hash)`](#GovernorCoreExecutionComponent-queue_operations) +- [`proposal_needs_queuing(self, proposal_id)`](#GovernorCoreExecutionComponent-proposal_needs_queuing) +- [`cancel_operations(self, proposal_id, description_hash)`](#GovernorCoreExecutionComponent-cancel_operations) + +#### [](#GovernorCoreExecutionComponent-Extension-Traits-Functions)Extension traits functions + +#### [](#GovernorCoreExecutionComponent-state)`state(self: @ContractState, proposal_id: felt252) → ProposalState` internal + +Returns the state of a proposal given its id. + +Requirements: + +- The proposal must exist. + +#### [](#GovernorCoreExecutionComponent-executor)`executor(self: @ContractState) → ContractAddress` internal + +Returns the executor address. + +In this case, it returns the governor contract address since execution is performed directly through it. + +#### [](#GovernorCoreExecutionComponent-execute_operations)`execute_operations(ref self: ContractState, proposal_id: felt252, calls: Span, description_hash: felt252)` internal + +Executes the proposal’s operations directly through the governor contract. + +#### [](#GovernorCoreExecutionComponent-queue_operations)`queue_operations(ref self: ContractState, proposal_id: felt252, calls: Span, description_hash: felt252) → u64` internal + +In this implementation, queuing is not required so it returns 0. + +#### [](#GovernorCoreExecutionComponent-proposal_needs_queuing)`proposal_needs_queuing(self: @ContractState, proposal_id: felt252) → bool` internal + +In this implementation, it always returns false. + +#### [](#GovernorCoreExecutionComponent-cancel_operations)`cancel_operations(ref self: ContractState, proposal_id: felt252, description_hash: felt252)` internal + +Cancels a proposal’s operations. + +### [](#GovernorCountingSimpleComponent)`GovernorCountingSimpleComponent`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/governance/src/governor/extensions/governor_counting_simple.cairo) + +```cairo +use openzeppelin_governance::governor::extensions::GovernorCountingSimpleComponent; +``` + +Extension of [GovernorComponent](#GovernorComponent) for simple vote counting with three options. + +Extension traits implementations + +GovernorCounting + +- [`counting_mode(self)`](#GovernorCountingSimpleComponent-counting_mode) +- [`count_vote(self, proposal_id, account, support, total_weight, params)`](#GovernorCountingSimpleComponent-count_vote) +- [`has_voted(self, proposal_id, account)`](#GovernorCountingSimpleComponent-has_voted) +- [`quorum_reached(self, proposal_id)`](#GovernorCountingSimpleComponent-quorum_reached) +- [`vote_succeeded(self, proposal_id)`](#GovernorCountingSimpleComponent-vote_succeeded) + +#### [](#GovernorCountingSimpleComponent-Extension-Traits-Functions)Extension traits functions + +#### [](#GovernorCountingSimpleComponent-counting_mode)`counting_mode(self: @ContractState) → ByteArray` internal + +Returns `"support=bravo&quorum=for,abstain"`. + +- `support=bravo` indicates that the support follows the Governor Bravo format where voters can vote For, Against, or Abstain +- `quorum=for,abstain` indicates that both For and Abstain votes count toward quorum + +#### [](#GovernorCountingSimpleComponent-count_vote)`count_vote(ref self: ContractState, proposal_id: felt252, account: ContractAddress, support: u8, total_weight: u256, params: Span) → u256` internal + +Records a vote for a proposal. + +The support value follows the `VoteType` enum (0=Against, 1=For, 2=Abstain). + +Returns the weight that was counted. + +#### [](#GovernorCountingSimpleComponent-has_voted)`has_voted(self: @ContractState, proposal_id: felt252, account: ContractAddress) → bool` internal + +Returns whether an account has cast a vote on a proposal. + +#### [](#GovernorCountingSimpleComponent-quorum_reached)`quorum_reached(self: @ContractState, proposal_id: felt252) → bool` internal + +Returns whether a proposal has reached quorum. + +In this implementation, both For and Abstain votes count toward quorum. + +#### [](#GovernorCountingSimpleComponent-vote_succeeded)`vote_succeeded(self: @ContractState, proposal_id: felt252) → bool` internal + +Returns whether a proposal has succeeded. + +In this implementation, the For votes must be strictly greater than Against votes. + +### [](#GovernorSettingsComponent)`GovernorSettingsComponent`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/governance/src/governor/extensions/governor_settings.cairo) + +```cairo +use openzeppelin_governance::governor::extensions::GovernorSettingsComponent; +``` + +Extension of [GovernorComponent](#GovernorComponent) for settings that are updatable through governance. + +Extension traits implementations + +GovernorSettings + +- [`voting_delay(self)`](#GovernorSettingsComponent-voting_delay) +- [`voting_period(self)`](#GovernorSettingsComponent-voting_period) +- [`proposal_threshold(self)`](#GovernorSettingsComponent-proposal_threshold) + +Embeddable implementations + +GovernorSettingsAdminImpl + +- [`set_voting_delay(self, new_voting_delay)`](#GovernorSettingsComponent-set_voting_delay) +- [`set_voting_period(self, new_voting_period)`](#GovernorSettingsComponent-set_voting_period) +- [`set_proposal_threshold(self, new_proposal_threshold)`](#GovernorSettingsComponent-set_proposal_threshold) + +Internal implementations + +InternalImpl + +- [`initializer(self, new_voting_delay, new_voting_period, new_proposal_threshold)`](#GovernorSettingsComponent-initializer) +- [`assert_only_governance(self)`](#GovernorSettingsComponent-assert_only_governance) +- [`_set_voting_delay(self, new_voting_delay)`](#GovernorSettingsComponent-_set_voting_delay) +- [`_set_voting_period(self, new_voting_period)`](#GovernorSettingsComponent-_set_voting_period) +- [`_set_proposal_threshold(self, new_proposal_threshold)`](#GovernorSettingsComponent-_set_proposal_threshold) + +Events + +- [`VotingDelayUpdated(old_voting_delay, new_voting_delay)`](#GovernorSettingsComponent-VotingDelayUpdated) +- [`VotingPeriodUpdated(old_voting_period, new_voting_period)`](#GovernorSettingsComponent-VotingPeriodUpdated) +- [`ProposalThresholdUpdated(old_proposal_threshold, new_proposal_threshold)`](#GovernorSettingsComponent-ProposalThresholdUpdated) + +#### [](#GovernorSettings-Extension-Traits-Functions)Extension traits functions + +#### [](#GovernorSettingsComponent-voting_delay)`voting_delay(self: @ContractState) → u64` internal + +Returns the delay, between when a proposal is created and when voting starts. + +#### [](#GovernorSettingsComponent-voting_period)`voting_period(self: @ContractState) → u64` internal + +Returns the time period, during which votes can be cast. + +#### [](#GovernorSettingsComponent-proposal_threshold)`proposal_threshold(self: @ContractState) → u256` internal + +Returns the minimum number of votes required for an account to create a proposal. + +#### [](#GovernorSettings-Embeddable-Functions)Embeddable functions + +#### [](#GovernorSettingsComponent-set_voting_delay)`set_voting_delay(ref self: ContractState, new_voting_delay: u64)` external + +Sets the voting delay. + +Requirements: + +- Caller must be the governance executor. + +This function does not emit an event if the new voting delay is the same as the old one. + +May emit a [VotingDelayUpdated](#GovernorSettingsComponent-VotingDelayUpdated) event. + +#### [](#GovernorSettingsComponent-set_voting_period)`set_voting_period(ref self: ContractState, new_voting_period: u64)` external + +Sets the voting period. + +This function does not emit an event if the new voting period is the same as the old one. + +Requirements: + +- Caller must be the governance executor. +- `new_voting_period` must be greater than 0. + +May emit a [VotingPeriodUpdated](#GovernorSettingsComponent-VotingPeriodUpdated) event. + +#### [](#GovernorSettingsComponent-set_proposal_threshold)`set_proposal_threshold(ref self: ContractState, new_proposal_threshold: u256)` external + +Sets the proposal threshold. + +This function does not emit an event if the new proposal threshold is the same as the old one. + +Requirements: + +- Caller must be the governance executor. + +May emit a [ProposalThresholdUpdated](#GovernorSettingsComponent-ProposalThresholdUpdated) event. + +#### [](#GovernorSettingsComponent-Internal-Functions)Internal functions + +#### [](#GovernorSettingsComponent-initializer)`initializer(ref self: ContractState, initial_voting_delay: u64, initial_voting_period: u64, initial_proposal_threshold: u256)` internal + +Initializes the component by setting the default values. + +Requirements: + +- `new_voting_period` must be greater than 0. + +Emits a [VotingDelayUpdated](#GovernorSettingsComponent-VotingDelayUpdated), [VotingPeriodUpdated](#GovernorSettingsComponent-VotingPeriodUpdated), and [ProposalThresholdUpdated](#GovernorSettingsComponent-ProposalThresholdUpdated) event. + +#### [](#GovernorSettingsComponent-assert_only_governance)`assert_only_governance(ref self: ContractState)` internal + +Asserts that the caller is the governance executor. + +#### [](#GovernorSettingsComponent-_set_voting_delay)`_set_voting_delay(ref self: ContractState, new_voting_delay: u64)` internal + +Internal function to update the voting delay. + +This function does not emit an event if the new voting delay is the same as the old one. + +May emit a [VotingDelayUpdated](#GovernorSettingsComponent-VotingDelayUpdated) event. + +#### [](#GovernorSettingsComponent-_set_voting_period)`_set_voting_period(ref self: ContractState, new_voting_period: u64)` internal + +Internal function to update the voting period. + +Requirements: + +- `new_voting_period` must be greater than 0. + +This function does not emit an event if the new voting period is the same as the old one. + +May emit a [VotingPeriodUpdated](#GovernorSettingsComponent-VotingPeriodUpdated) event. + +#### [](#GovernorSettingsComponent-_set_proposal_threshold)`_set_proposal_threshold(ref self: ContractState, new_proposal_threshold: u256)` internal + +Internal function to update the proposal threshold. + +This function does not emit an event if the new proposal threshold is the same as the old one. + +May emit a [ProposalThresholdUpdated](#GovernorSettingsComponent-ProposalThresholdUpdated) event. + +#### [](#GovernorSettings-Events)Events + +#### [](#GovernorSettingsComponent-VotingDelayUpdated)`VotingDelayUpdated(old_voting_delay: u64, new_voting_delay: u64)` event + +Emitted when the voting delay is updated. + +#### [](#GovernorSettingsComponent-VotingPeriodUpdated)`VotingPeriodUpdated(old_voting_period: u64, new_voting_period: u64)` event + +Emitted when the voting period is updated. + +#### [](#GovernorSettingsComponent-ProposalThresholdUpdated)`ProposalThresholdUpdated(old_proposal_threshold: u256, new_proposal_threshold: u256)` event + +Emitted when the proposal threshold is updated. + +### [](#GovernorVotesComponent)`GovernorVotesComponent`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/governance/src/governor/extensions/governor_votes.cairo) + +```cairo +use openzeppelin_governance::governor::extensions::GovernorVotesComponent; +``` + +Extension of [GovernorComponent](#GovernorComponent) for voting weight extraction from a token with the [IVotes](#IVotes) extension. + +Extension traits implementations + +GovernorVotes + +- [`clock(self)`](#GovernorVotesComponent-clock) +- [`CLOCK_MODE(self)`](#GovernorVotesComponent-CLOCK_MODE) +- [`get_votes(self, account, timepoint, params)`](#GovernorVotesComponent-get_votes) + +Embeddable implementations + +VotesTokenImpl + +- [`token(self)`](#GovernorVotesComponent-token) + +Internal implementations + +InternalImpl + +- [`initializer(self, votes_token)`](#GovernorVotesComponent-initializer) + +#### [](#GovernorVotes-Extension-Traits-Functions)Extension traits functions + +#### [](#GovernorVotesComponent-clock)`clock(self: @ContractState) → u64` internal + +Returns the current timepoint determined by the governor’s operational mode, intended for use in time-sensitive logic. See [ERC-6372#clock](https://eips.ethereum.org/EIPS/eip-6372#clock). + +Requirements: + +- This function MUST always be non-decreasing. + +#### [](#GovernorVotesComponent-CLOCK_MODE)`CLOCK_MODE(self: @ContractState) → ByteArray` internal + +Returns a description of the clock the governor is operating in. See [ERC-6372#CLOCK\_MODE](https://eips.ethereum.org/EIPS/eip-6372#clock_mode). + +Requirements: + +- The output MUST be formatted like a URL query string, decodable in standard JavaScript. + +#### [](#GovernorVotesComponent-get_votes)`get_votes(self: @ContractState, account: ContractAddress, timepoint: u64, params: Span) → u256` internal + +Returns the voting power of `account` at a specific `timepoint` using the votes token. + +#### [](#GovernorVotesComponent-Embeddable-Functions)Embeddable functions + +#### [](#GovernorVotesComponent-token)`token(self: @ContractState) → ContractAddress` external + +Returns the votes token that voting power is sourced from. + +#### [](#GovernorVotesComponent-Internal-Functions)Internal functions + +#### [](#GovernorVotesComponent-initializer)`initializer(ref self: ContractState, votes_token: ContractAddress)` internal + +Initializes the component by setting the votes token. + +Requirements: + +- `votes_token` must not be zero. + +### [](#GovernorVotesQuorumFractionComponent)`GovernorVotesQuorumFractionComponent`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/governance/src/governor/extensions/governor_votes_quorum_fraction.cairo) + +```cairo +use openzeppelin_governance::governor::extensions::GovernorVotesQuorumFractionComponent; +``` + +Extension of [GovernorComponent](#GovernorComponent) for voting weight extraction from a token with the [IVotes](#IVotes) extension and a quorum expressed as a fraction of the total supply. + +Extension traits implementations + +GovernorQuorum + +- [`quorum(self, timepoint)`](#GovernorVotesQuorumFractionComponent-quorum) + +GovernorVotes + +- [`clock(self)`](#GovernorVotesQuorumFractionComponent-clock) +- [`CLOCK_MODE(self)`](#GovernorVotesQuorumFractionComponent-CLOCK_MODE) +- [`get_votes(self, account, timepoint, params)`](#GovernorVotesQuorumFractionComponent-get_votes) + +Embeddable implementations + +QuorumFractionImpl + +- [`token(self)`](#GovernorVotesQuorumFractionComponent-token) +- [`current_quorum_numerator(self)`](#GovernorVotesQuorumFractionComponent-current_quorum_numerator) +- [`quorum_numerator(self, timepoint)`](#GovernorVotesQuorumFractionComponent-quorum_numerator) +- [`quorum_denominator(self)`](#GovernorVotesQuorumFractionComponent-quorum_denominator) + +Internal implementations + +InternalImpl + +- [`initializer(self, votes_token, quorum_numerator)`](#GovernorVotesQuorumFractionComponent-initializer) +- [`update_quorum_numerator(self, new_quorum_numerator)`](#GovernorVotesQuorumFractionComponent-update_quorum_numerator) + +Events + +- [`QuorumNumeratorUpdated(old_quorum_numerator, new_quorum_numerator)`](#GovernorVotesQuorumFractionComponent-QuorumNumeratorUpdated) + +#### [](#GovernorVotesQuorumFractionComponent-Extension-Traits-Functions)Extension traits functions + +#### [](#GovernorVotesQuorumFractionComponent-quorum)`quorum(self: @ContractState, timepoint: u64) → u256` internal + +It is computed as a percentage of the votes token total supply at a given `timepoint` in the past. + +#### [](#GovernorVotesQuorumFractionComponent-clock)`clock(self: @ContractState) → u64` internal + +Returns the current timepoint determined by the governor’s operational mode, intended for use in time-sensitive logic. See [ERC-6372#clock](https://eips.ethereum.org/EIPS/eip-6372#clock). + +Requirements: + +- This function MUST always be non-decreasing. + +#### [](#GovernorVotesQuorumFractionComponent-CLOCK_MODE)`CLOCK_MODE(self: @ContractState) → ByteArray` internal + +Returns a description of the clock the governor is operating in. See [ERC-6372#CLOCK\_MODE](https://eips.ethereum.org/EIPS/eip-6372#clock_mode). + +Requirements: + +- The output MUST be formatted like a URL query string, decodable in standard JavaScript. + +#### [](#GovernorVotesQuorumFractionComponent-get_votes)`get_votes(self: @ContractState, account: ContractAddress, timepoint: u64, params: Span) → u256` internal + +Returns the voting power of `account` at a specific `timepoint` using the votes token. + +#### [](#GovernorVotesQuorumFractionComponent-Embeddable-Functions)Embeddable functions + +#### [](#GovernorVotesQuorumFractionComponent-token)`token(self: @ContractState) → ContractAddress` external + +Returns the address of the votes token used for voting power extraction. + +#### [](#GovernorVotesQuorumFractionComponent-current_quorum_numerator)`current_quorum_numerator(self: @ContractState) → u256` external + +Returns the current quorum numerator value. + +#### [](#GovernorVotesQuorumFractionComponent-quorum_numerator)`quorum_numerator(self: @ContractState, timepoint: u64) → u256` external + +Returns the quorum numerator value at a specific `timepoint` in the past. + +#### [](#GovernorVotesQuorumFractionComponent-quorum_denominator)`quorum_denominator(self: @ContractState) → u256` external + +Returns the quorum denominator value. + +#### [](#GovernorVotesQuorumFractionComponent-Internal-Functions)Internal functions + +#### [](#GovernorVotesQuorumFractionComponent-initializer)`initializer(self: @ComponentState, votes_token: ContractAddress, quorum_numerator: u256)` internal + +Initializes the component by setting the votes token and the initial quorum numerator value. + +Requirements: + +- `votes_token` must not be zero. +- `quorum_numerator` must be less than `quorum_denominator`. + +Emits a [QuorumNumeratorUpdated](#GovernorVotesQuorumFractionComponent-QuorumNumeratorUpdated) event. + +#### [](#GovernorVotesQuorumFractionComponent-update_quorum_numerator)`update_quorum_numerator(self: @ComponentState, new_quorum_numerator: u256)` internal + +Updates the quorum numerator. + +This function does not emit an event if the new quorum numerator is the same as the old one. + +Requirements: + +- `new_quorum_numerator` must be less than `quorum_denominator`. + +May emit a [QuorumNumeratorUpdated](#GovernorVotesQuorumFractionComponent-QuorumNumeratorUpdated) event. + +#### [](#GovernorVotesQuorumFractionComponent-Events)Events + +#### [](#GovernorVotesQuorumFractionComponent-QuorumNumeratorUpdated)`QuorumNumeratorUpdated(old_quorum_numerator: u256, new_quorum_numerator: u256)` event + +Emitted when the quorum numerator is updated. + +### [](#GovernorTimelockExecutionComponent)`GovernorTimelockExecutionComponent`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/governance/src/governor/extensions/governor_timelock_execution.cairo) + +```cairo +use openzeppelin_governance::governor::extensions::GovernorTimelockExecutionComponent; +``` + +Extension of [GovernorComponent](#GovernorComponent) that binds the execution process to an instance of a contract implementing [TimelockControllerComponent](#TimelockControllerComponent). This adds a delay, enforced by the timelock to all successful proposals (in addition to the voting duration). + +The Governor needs the [PROPOSER, EXECUTOR, and CANCELLER roles](../governance/timelock#roles) to work properly. + +Using this model means the proposal will be operated by the timelock and not by the governor. Thus, the assets and permissions must be attached to the timelock. Any asset sent to the governor will be inaccessible from a proposal, unless executed via `Governor::relay`. + +Setting up the timelock to have additional proposers or cancellers besides the governor is very risky, as it grants them the ability to: 1) execute operations as the timelock, and thus possibly performing operations or accessing funds that are expected to only be accessible through a vote, and 2) block governance proposals that have been approved by the voters, effectively executing a Denial of Service attack. + +Extension traits implementations + +GovernorExecution + +- [`state(self, proposal_id)`](#GovernorTimelockExecutionComponent-state) +- [`executor(self)`](#GovernorTimelockExecutionComponent-executor) +- [`execute_operations(self, proposal_id, calls, description_hash)`](#GovernorTimelockExecutionComponent-execute_operations) +- [`queue_operations(self, proposal_id, calls, description_hash)`](#GovernorTimelockExecutionComponent-queue_operations) +- [`proposal_needs_queuing(self, proposal_id)`](#GovernorTimelockExecutionComponent-proposal_needs_queuing) +- [`cancel_operations(self, proposal_id, description_hash)`](#GovernorTimelockExecutionComponent-cancel_operations) + +Embeddable implementations + +TimelockedImpl + +- [`timelock(self)`](#GovernorTimelockExecutionComponent-timelock) +- [`get_timelock_id(self, proposal_id)`](#GovernorTimelockExecutionComponent-get_timelock_id) +- [`update_timelock(self, new_timelock)`](#GovernorTimelockExecutionComponent-update_timelock) + +Internal implementations + +InternalImpl + +- [`initializer(self, timelock_controller)`](#GovernorTimelockExecutionComponent-initializer) +- [`assert_only_governance(self)`](#GovernorTimelockExecutionComponent-assert_only_governance) +- [`timelock_salt(self, description_hash)`](#GovernorTimelockExecutionComponent-timelock_salt) +- [`get_timelock_dispatcher(self)`](#GovernorTimelockExecutionComponent-get_timelock_dispatcher) +- [`_update_timelock(self, new_timelock)`](#GovernorTimelockExecutionComponent-_update_timelock) + +Events + +- [`TimelockUpdated(old_timelock, new_timelock)`](#GovernorTimelockExecutionComponent-TimelockUpdated) + +#### [](#GovernorTimelockExecutionComponent-Extension-Traits-Functions)Extension traits functions + +#### [](#GovernorTimelockExecutionComponent-state)`state(self: @ContractState, proposal_id: felt252) → ProposalState` internal + +Returns the state of a proposal given its id. + +Requirements: + +- The proposal must exist. + +#### [](#GovernorTimelockExecutionComponent-executor)`executor(self: @ContractState) → ContractAddress` internal + +Returns the executor address. + +In this module, the executor is the timelock controller. + +#### [](#GovernorTimelockExecutionComponent-execute_operations)`execute_operations(ref self: ContractState, proposal_id: felt252, calls: Span, description_hash: felt252)` internal + +Runs the already queued proposal through the timelock. + +#### [](#GovernorTimelockExecutionComponent-queue_operations)`queue_operations(ref self: ContractState, proposal_id: felt252, calls: Span, description_hash: felt252) → u64` internal + +Queue a proposal to the timelock. + +Returns the eta for the execution of the queued proposal. + +#### [](#GovernorTimelockExecutionComponent-proposal_needs_queuing)`proposal_needs_queuing(self: @ContractState, proposal_id: felt252) → bool` internal + +In this implementation, it always returns true. + +#### [](#GovernorTimelockExecutionComponent-cancel_operations)`cancel_operations(ref self: ContractState, proposal_id: felt252, description_hash: felt252)` internal + +Cancels the timelocked proposal if it has already been queued. + +#### [](#GovernorTimelockExecutionComponent-Embeddable-Functions)Embeddable functions + +#### [](#GovernorTimelockExecutionComponent-timelock)`timelock(self: @ContractState) → ContractAddress` external + +Returns the timelock controller address. + +#### [](#GovernorTimelockExecutionComponent-get_timelock_id)`get_timelock_id(self: @ContractState) → felt252` external + +Returns the timelock proposal id for a given proposal id. + +#### [](#GovernorTimelockExecutionComponent-update_timelock)`update_timelock(ref self: ContractState, new_timelock: ContractAddress)` external + +Updates the associated timelock. + +Requirements: + +- The caller must be the governance. + +Emits a [TimelockUpdated](#GovernorTimelockExecutionComponent-TimelockUpdated) event. + +#### [](#GovernorTimelockExecutionComponent-Internal-Functions)Internal functions + +#### [](#GovernorTimelockExecutionComponent-initializer)`initializer(ref self: ContractState, timelock: ContractAddress)` internal + +Initializes the timelock controller. + +Requirements: + +- The timelock must not be the zero address. + +#### [](#GovernorTimelockExecutionComponent-assert_only_governance)`assert_only_governance(self: @ContractState)` internal + +Ensures the caller is the executor (the timelock controller in this case). + +#### [](#GovernorTimelockExecutionComponent-timelock_salt)`timelock_salt(self: @ContractState, description_hash: felt252) → felt252` internal + +Computes the `TimelockController` operation salt as the XOR of the governor address and `description_hash`. + +It is computed with the governor address itself to avoid collisions across governor instances using the same timelock. + +#### [](#GovernorTimelockExecutionComponent-get_timelock_dispatcher)`get_timelock_dispatcher(self: @ContractState) → ITimelockDispatcher` internal + +Returns a dispatcher for interacting with the timelock controller. + +#### [](#GovernorTimelockExecutionComponent-_update_timelock)`_update_timelock(ref self: ContractState, new_timelock: ContractAddress)` internal + +Internal function to update the timelock controller address. + +Emits a [TimelockUpdated](#GovernorTimelockExecutionComponent-TimelockUpdated) event. + +#### [](#GovernorTimelockExecutionComponent-Events)Events + +#### [](#GovernorTimelockExecutionComponent-TimelockUpdated)`TimelockUpdated(old_timelock: ContractAddress, new_timelock: ContractAddress)` event + +Emitted when the timelock controller is updated. + +## [](#multisig)Multisig + +A Multisig module enhances security and decentralization by requiring multiple signers to approve and execute transactions. Features include configurable quorum, signer management, and self-administration, ensuring collective decision-making and transparency for critical operations. + +### [](#MultisigComponent)`MultisigComponent` + +```cairo +use openzeppelin_governance::multisig::MultisigComponent; +``` + +Component that implements [IMultisig](#IMultisig) and provides functionality for multisignature wallets, including transaction management, quorum handling, and signer operations. + +Embeddable Implementations + +MultisigImpl + +- [`get_quorum(self)`](#MultisigComponent-get_quorum) +- [`is_signer(self, signer)`](#MultisigComponent-is_signer) +- [`get_signers(self)`](#MultisigComponent-get_signers) +- [`is_confirmed(self, id)`](#MultisigComponent-is_confirmed) +- [`is_confirmed_by(self, id, signer)`](#MultisigComponent-is_confirmed_by) +- [`is_executed(self, id)`](#MultisigComponent-is_executed) +- [`get_submitted_block(self, id)`](#MultisigComponent-get_submitted_block) +- [`get_transaction_state(self, id)`](#MultisigComponent-get_transaction_state) +- [`get_transaction_confirmations(self, id)`](#MultisigComponent-get_transaction_confirmations) +- [`hash_transaction(self, to, selector, calldata, salt)`](#MultisigComponent-hash_transaction) +- [`hash_transaction_batch(self, calls, salt)`](#MultisigComponent-hash_transaction_batch) +- [`add_signers(ref self, new_quorum, signers_to_add)`](#MultisigComponent-add_signers) +- [`remove_signers(ref self, new_quorum, signers_to_remove)`](#MultisigComponent-remove_signers) +- [`replace_signer(ref self, signer_to_remove, signer_to_add)`](#MultisigComponent-replace_signer) +- [`change_quorum(ref self, new_quorum)`](#MultisigComponent-change_quorum) +- [`submit_transaction(ref self, to, selector, calldata, salt)`](#MultisigComponent-submit_transaction) +- [`submit_transaction_batch(ref self, calls, salt)`](#MultisigComponent-submit_transaction_batch) +- [`confirm_transaction(ref self, id)`](#MultisigComponent-confirm_transaction) +- [`revoke_confirmation(ref self, id)`](#MultisigComponent-revoke_confirmation) +- [`execute_transaction(ref self, to, selector, calldata, salt)`](#MultisigComponent-execute_transaction) +- [`execute_transaction_batch(ref self, calls, salt)`](#MultisigComponent-execute_transaction_batch) + +Internal Implementations + +InternalImpl + +- [`initializer(ref self, quorum, signers)`](#MultisigComponent-initializer) +- [`resolve_tx_state(self, id)`](#MultisigComponent-resolve_tx_state) +- [`assert_one_of_signers(self, caller)`](#MultisigComponent-assert_one_of_signers) +- [`assert_tx_exists(self, id)`](#MultisigComponent-assert_tx_exists) +- [`assert_only_self(self)`](#MultisigComponent-assert_only_self) +- [`_add_signers(ref self, new_quorum, signers_to_add)`](#MultisigComponent-_add_signers) +- [`_remove_signers(ref self, new_quorum, signers_to_remove)`](#MultisigComponent-_remove_signers) +- [`_replace_signer(ref self, signer_to_remove, signer_to_add)`](#MultisigComponent-_replace_signer) +- [`_change_quorum(ref self, new_quorum)`](#MultisigComponent-_change_quorum) + +Events + +- [`SignerAdded(signer)`](#MultisigComponent-SignerAdded) +- [`SignerRemoved(signer)`](#MultisigComponent-SignerRemoved) +- [`QuorumUpdated(old_quorum, new_quorum)`](#MultisigComponent-QuorumUpdated) +- [`TransactionSubmitted(id, signer)`](#MultisigComponent-TransactionSubmitted) +- [`TransactionConfirmed(id, signer)`](#MultisigComponent-TransactionConfirmed) +- [`ConfirmationRevoked(id, signer)`](#MultisigComponent-ConfirmationRevoked) +- [`TransactionExecuted(id)`](#MultisigComponent-TransactionExecuted) +- [`CallSalt(id, salt)`](#MultisigComponent-CallSalt) + +#### [](#MultisigComponent-Functions)Embeddable functions + +#### [](#MultisigComponent-get_quorum)`get_quorum(self: @ContractState) → u32` external + +Returns the current quorum value. + +#### [](#MultisigComponent-is_signer)`is_signer(self: @ContractState, signer: ContractAddress) → bool` external + +Checks if a given `signer` is registered. + +#### [](#MultisigComponent-get_signers)`get_signers(self: @ContractState) → Span` external + +Returns a list of all current signers. + +#### [](#MultisigComponent-is_confirmed)`is_confirmed(self: @ContractState, id: TransactionID) → bool` external + +Returns whether the transaction with the given `id` has been confirmed. A confirmed transaction has received the required number of confirmations (quorum). + +#### [](#MultisigComponent-is_confirmed_by)`is_confirmed_by(self: @ContractState, id: TransactionID, signer: ContractAddress) → bool` external + +Returns whether the transaction with the given `id` has been confirmed by the specified `signer`. + +#### [](#MultisigComponent-is_executed)`is_executed(self: @ContractState, id: TransactionID) → bool` external + +Returns whether the transaction with the given `id` has been executed. + +#### [](#MultisigComponent-get_submitted_block)`get_submitted_block(self: @ContractState, id: TransactionID) → u64` external + +Returns the block number when the transaction with the given `id` was submitted. + +#### [](#MultisigComponent-get_transaction_state)`get_transaction_state(self: @ContractState, id: TransactionID) → TransactionState` external + +Returns the current state of the transaction with the given `id`. + +The possible states are: + +- `NotFound`: the transaction does not exist. +- `Pending`: the transaction exists but hasn’t reached the required confirmations. +- `Confirmed`: the transaction has reached the required confirmations but hasn’t been executed. +- `Executed`: the transaction has been executed. + +#### [](#MultisigComponent-get_transaction_confirmations)`get_transaction_confirmations(self: @ContractState, id: TransactionID) → u32` external + +Returns the number of confirmations from registered signers for the transaction with the specified `id`. + +#### [](#MultisigComponent-hash_transaction)`hash_transaction(self: @ContractState, to: ContractAddress, selector: felt252, calldata: Span, salt: felt252)` external + +Returns the computed identifier of a transaction containing a single call. + +#### [](#MultisigComponent-hash_transaction_batch)`hash_transaction_batch(self: @ContractState, calls: Span, salt: felt252)` external + +Returns the computed identifier of a transaction containing a batch of calls. + +#### [](#MultisigComponent-add_signers)`add_signers(ref self: ContractState, new_quorum: u32, signers_to_add: Span)` external + +Adds new signers and updates the quorum. + +Requirements: + +- The caller must be the contract itself. +- `new_quorum` must be less than or equal to the total number of signers after addition. + +Emits a [SignerAdded](#MultisigComponent-SignerAdded) event for each signer added. + +Emits a [QuorumUpdated](#MultisigComponent-QuorumUpdated) event if the quorum changes. + +#### [](#MultisigComponent-remove_signers)`remove_signers(ref self: ContractState, new_quorum: u32, signers_to_remove: Span)` external + +Removes signers and updates the quorum. + +Requirements: + +- The caller must be the contract itself. +- `new_quorum` must be less than or equal to the total number of signers after removal. + +Emits a [SignerRemoved](#MultisigComponent-SignerRemoved) event for each signer removed. + +Emits a [QuorumUpdated](#MultisigComponent-QuorumUpdated) event if the quorum changes. + +#### [](#MultisigComponent-replace_signer)`replace_signer(ref self: ContractState, signer_to_remove: ContractAddress, signer_to_add: ContractAddress)` external + +Replaces an existing signer with a new signer. + +Requirements: + +- The caller must be the contract itself. +- `signer_to_remove` must be an existing signer. +- `signer_to_add` must not be an existing signer. + +Emits a [SignerRemoved](#MultisigComponent-SignerRemoved) event for the removed signer. + +Emits a [SignerAdded](#MultisigComponent-SignerAdded) event for the new signer. + +#### [](#MultisigComponent-change_quorum)`change_quorum(ref self: ContractState, new_quorum: u32)` external + +Updates the quorum value to `new_quorum`. + +Requirements: + +- The caller must be the contract itself. +- `new_quorum` must be non-zero. +- `new_quorum` must be less than or equal to the total number of signers. + +Emits a [QuorumUpdated](#MultisigComponent-QuorumUpdated) event if the quorum changes. + +#### [](#MultisigComponent-submit_transaction)`submit_transaction(ref self: ContractState, to: ContractAddress, selector: felt252, calldata: Span, salt: felt252)` external + +Submits a new transaction for confirmation. + +Requirements: + +- The caller must be a registered signer. +- The transaction must not have been submitted before. + +Emits a [TransactionSubmitted](#MultisigComponent-TransactionSubmitted) event. + +Emits a [CallSalt](#MultisigComponent-CallSalt) event if `salt` is not zero. + +#### [](#MultisigComponent-submit_transaction_batch)`submit_transaction_batch(ref self: ContractState, calls: Span, salt: felt252)` external + +Submits a new batch transaction for confirmation. + +Requirements: + +- The caller must be a registered signer. +- The transaction must not have been submitted before. + +Emits a [TransactionSubmitted](#MultisigComponent-TransactionSubmitted) event. + +Emits a [CallSalt](#MultisigComponent-CallSalt) event if `salt` is not zero. + +#### [](#MultisigComponent-confirm_transaction)`confirm_transaction(ref self: ContractState, id: TransactionID)` external + +Confirms a transaction with the given `id`. + +Requirements: + +- The caller must be a registered signer. +- The transaction must exist and not be executed. +- The caller must not have already confirmed the transaction. + +Emits a [TransactionConfirmed](#MultisigComponent-TransactionConfirmed) event. + +#### [](#MultisigComponent-revoke_confirmation)`revoke_confirmation(ref self: ContractState, id: TransactionID)` external + +Revokes a previous confirmation for a transaction with the given `id`. + +Requirements: + +- The transaction must exist and not be executed. +- The caller must have previously confirmed the transaction. + +Emits a [ConfirmationRevoked](#MultisigComponent-ConfirmationRevoked) event. + +#### [](#MultisigComponent-execute_transaction)`execute_transaction(ref self: ContractState, to: ContractAddress, selector: felt252, calldata: Span, salt: felt252)` external + +Executes a confirmed transaction. + +Requirements: + +- The caller must be a registered signer. +- The transaction must be confirmed and not yet executed. + +Emits a [TransactionExecuted](#MultisigComponent-TransactionExecuted) event. + +#### [](#MultisigComponent-execute_transaction_batch)`execute_transaction_batch(ref self: ContractState, calls: Span, salt: felt252)` external + +Executes a confirmed batch transaction. + +Requirements: + +- The caller must be a registered signer. +- The transaction must be confirmed and not yet executed. + +Emits a [TransactionExecuted](#MultisigComponent-TransactionExecuted) event. + +#### [](#MultisigComponent-Internal-Functions)Internal functions + +#### [](#MultisigComponent-initializer)`initializer(ref self: ContractState, quorum: u32, signers: Span)` internal + +Initializes the Multisig component with the initial `quorum` and `signers`. This function must be called during contract initialization to set up the initial state. + +Requirements: + +- `quorum` must be non-zero and less than or equal to the number of `signers`. + +Emits a [SignerAdded](#MultisigComponent-SignerAdded) event for each signer added. + +Emits a [QuorumUpdated](#MultisigComponent-QuorumUpdated) event. + +#### [](#MultisigComponent-resolve_tx_state)`resolve_tx_state(self: @ContractState, id: TransactionID) → TransactionState` internal + +Resolves and returns the current state of the transaction with the given `id`. + +The possible states are: + +- `NotFound`: the transaction does not exist. +- `Pending`: the transaction exists but hasn’t reached the required confirmations. +- `Confirmed`: the transaction has reached the required confirmations but hasn’t been executed. +- `Executed`: the transaction has been executed. + +#### [](#MultisigComponent-assert_one_of_signers)`assert_one_of_signers(self: @ContractState, caller: ContractAddress)` internal + +Asserts that the `caller` is one of the registered signers. + +Requirements: + +- The `caller` must be a registered signer. + +#### [](#MultisigComponent-assert_tx_exists)`assert_tx_exists(self: @ContractState, id: TransactionID)` internal + +Asserts that a transaction with the given `id` exists. + +Requirements: + +- The transaction with the given `id` must have been submitted. + +#### [](#MultisigComponent-assert_only_self)`assert_only_self(self: @ContractState)` internal + +Asserts that the caller is the contract itself. + +Requirements: + +- The caller must be the contract’s own address. + +#### [](#MultisigComponent-_add_signers)`_add_signers(ref self: ContractState, new_quorum: u32, signers_to_add: Span)` internal + +Adds new signers and updates the quorum. + +Requirements: + +- Each signer address must be non-zero. +- `new_quorum` must be non-zero and less than or equal to the total number of signers after addition. + +Emits a [SignerAdded](#MultisigComponent-SignerAdded) event for each new signer added. + +Emits a [QuorumUpdated](#MultisigComponent-QuorumUpdated) event if the quorum changes. + +#### [](#MultisigComponent-_remove_signers)`_remove_signers(ref self: ContractState, new_quorum: u32, signers_to_remove: Span)` internal + +Removes existing signers and updates the quorum. + +Requirements: + +- `new_quorum` must be non-zero and less than or equal to the total number of signers after removal. + +Emits a [SignerRemoved](#MultisigComponent-SignerRemoved) event for each signer removed. + +Emits a [QuorumUpdated](#MultisigComponent-QuorumUpdated) event if the quorum changes. + +#### [](#MultisigComponent-_replace_signer)`_replace_signer(ref self: ContractState, signer_to_remove: ContractAddress, signer_to_add: ContractAddress)` internal + +Replaces an existing signer with a new signer. + +Requirements: + +- `signer_to_remove` must be an existing signer. +- `signer_to_add` must not be an existing signer. +- `signer_to_add` must be a non-zero address. + +Emits a [SignerRemoved](#MultisigComponent-SignerRemoved) event for the removed signer. + +Emits a [SignerAdded](#MultisigComponent-SignerAdded) event for the new signer. + +#### [](#MultisigComponent-_change_quorum)`_change_quorum(ref self: ContractState, new_quorum: u32)` internal + +Updates the quorum value to `new_quorum` if it differs from the current quorum. + +Requirements: + +- `new_quorum` must be non-zero. +- `new_quorum` must be less than or equal to the total number of signers. + +Emits a [QuorumUpdated](#MultisigComponent-QuorumUpdated) event if the quorum changes. + +#### [](#MultisigComponent-Events)Events + +#### [](#MultisigComponent-SignerAdded)`SignerAdded(signer: ContractAddress)` event + +Emitted when a new `signer` is added. + +#### [](#MultisigComponent-SignerRemoved)`SignerRemoved(signer: ContractAddress)` event + +Emitted when a `signer` is removed. + +#### [](#MultisigComponent-QuorumUpdated)`QuorumUpdated(old_quorum: u32, new_quorum: u32)` event + +Emitted when the `quorum` value is updated. + +#### [](#MultisigComponent-TransactionSubmitted)`TransactionSubmitted(id: TransactionID, signer: ContractAddress)` event + +Emitted when a new transaction is submitted by a `signer`. + +#### [](#MultisigComponent-TransactionConfirmed)`TransactionConfirmed(id: TransactionID, signer: ContractAddress)` event + +Emitted when a transaction is confirmed by a `signer`. + +#### [](#MultisigComponent-ConfirmationRevoked)`ConfirmationRevoked(id: TransactionID, signer: ContractAddress)` event + +Emitted when a `signer` revokes his confirmation. + +#### [](#MultisigComponent-TransactionExecuted)`TransactionExecuted(id: TransactionID)` event + +Emitted when a transaction is executed. + +#### [](#MultisigComponent-CallSalt)`CallSalt(id: felt252, salt: felt252)` event + +Emitted when a new transaction is submitted with non-zero salt. + +## [](#timelock)Timelock + +In a governance system, `TimelockControllerComponent` is in charge of introducing a delay between a proposal and its execution. + +### [](#TimelockControllerComponent)`TimelockControllerComponent`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/governance/src/timelock/timelock_controller.cairo) + +```cairo +use openzeppelin_governance::timelock::TimelockControllerComponent; +``` + +Component that implements [ITimelock](#ITimelock) and enables the implementing contract to act as a timelock controller. + +[Embeddable Mixin Implementations](../components#mixins) + +TimelockMixinImpl + +- [`TimelockImpl`](#TimelockControllerComponent-Embeddable-Impls-TimelockImpl) +- [`SRC5Impl`](introspection#SRC5Component-Embeddable-Impls) +- [`AccessControlImpl`](access#AccessControlComponent-Embeddable-Impls) +- [`AccessControlCamelImpl`](access#AccessControlComponent-Embeddable-Impls) + +Embeddable Implementations + +TimelockImpl + +- [`is_operation(self, id)`](#TimelockControllerComponent-is_operation) +- [`is_operation_pending(self, id)`](#TimelockControllerComponent-is_operation_pending) +- [`is_operation_ready(self, id)`](#TimelockControllerComponent-is_operation_ready) +- [`is_operation_done(self, id)`](#TimelockControllerComponent-is_operation_done) +- [`get_timestamp(self, id)`](#TimelockControllerComponent-get_timestamp) +- [`get_operation_state(self, id)`](#TimelockControllerComponent-get_operation_state) +- [`get_min_delay(self)`](#TimelockControllerComponent-get_min_delay) +- [`hash_operation(self, call, predecessor, salt)`](#TimelockControllerComponent-hash_operation) +- [`hash_operation_batch(self, calls, predecessor, salt)`](#TimelockControllerComponent-hash_operation_batch) +- [`schedule(self, call, predecessor, salt, delay)`](#TimelockControllerComponent-schedule) +- [`schedule_batch(self, calls, predecessor, salt, delay)`](#TimelockControllerComponent-schedule_batch) +- [`cancel(self, id)`](#TimelockControllerComponent-cancel) +- [`execute(self, call, predecessor, salt)`](#TimelockControllerComponent-execute) +- [`execute_batch(self, calls, predecessor, salt)`](#TimelockControllerComponent-execute_batch) +- [`update_delay(self, new_delay)`](#TimelockControllerComponent-update_delay) + +SRC5Impl + +- [`supports_interface(self, interface_id: felt252)`](introspection#ISRC5-supports_interface) + +AccessControlImpl + +- [`has_role(self, role, account)`](access#IAccessControl-has_role) +- [`get_role_admin(self, role)`](access#IAccessControl-get_role_admin) +- [`grant_role(self, role, account)`](access#IAccessControl-grant_role) +- [`revoke_role(self, role, account)`](access#IAccessControl-revoke_role) +- [`renounce_role(self, role, account)`](access#IAccessControl-renounce_role) + +AccessControlCamelImpl + +- [`hasRole(self, role, account)`](access#IAccessControl-hasRole) +- [`getRoleAdmin(self, role)`](access#IAccessControl-getRoleAdmin) +- [`grantRole(self, role, account)`](access#IAccessControl-grantRole) +- [`revokeRole(self, role, account)`](access#IAccessControl-revokeRole) +- [`renounceRole(self, role, account)`](access#IAccessControl-renounceRole) + +Internal Implementations + +InternalImpl + +- [`initializer(self, min_delay, proposers, executors, admin)`](#TimelockControllerComponent-initializer) +- [`assert_only_role(self, role)`](#TimelockControllerComponent-assert_only_role) +- [`assert_only_role_or_open_role(self, role)`](#TimelockControllerComponent-assert_only_role_or_open_role) +- [`assert_only_self(self)`](#TimelockControllerComponent-assert_only_self) +- [`_before_call(self, id, predecessor)`](#TimelockControllerComponent-_before_call) +- [`_after_call(self, id)`](#TimelockControllerComponent-_after_call) +- [`_schedule(self, id, delay)`](#TimelockControllerComponent-_schedule) +- [`_execute(self, call)`](#TimelockControllerComponent-_execute) + +Events + +- [`CallScheduled(id, index, call, predecessor, delay)`](#TimelockControllerComponent-CallScheduled) +- [`CallExecuted(id, index, call)`](#TimelockControllerComponent-CallExecuted) +- [`CallSalt(id, salt)`](#TimelockControllerComponent-CallSalt) +- [`CallCancelled(id)`](#TimelockControllerComponent-CallCancelled) +- [`MinDelayChanged(old_duration, new_duration)`](#TimelockControllerComponent-MinDelayChanged) + +#### [](#TimelockControllerComponent-Functions)Embeddable functions + +#### [](#TimelockControllerComponent-is_operation)`is_operation(self: @ContractState, id: felt252) → bool` external + +Returns whether `id` corresponds to a registered operation. This includes the OperationStates: `Waiting`, `Ready`, and `Done`. + +#### [](#TimelockControllerComponent-is_operation_pending)`is_operation_pending(self: @ContractState, id: felt252) → bool` external + +Returns whether the `id` OperationState is pending or not. Note that a pending operation may be either `Waiting` or `Ready`. + +#### [](#TimelockControllerComponent-is_operation_ready)`is_operation_ready(self: @ContractState, id: felt252) → bool` external + +Returns whether the `id` OperationState is `Ready` or not. + +#### [](#TimelockControllerComponent-is_operation_done)`is_operation_done(self: @ContractState, id: felt252) → bool` external + +Returns whether the `id` OperationState is `Done` or not. + +#### [](#TimelockControllerComponent-get_timestamp)`get_timestamp(self: @ContractState, id: felt252) → u64` external + +Returns the timestamp at which `id` becomes `Ready`. + +`0` means the OperationState is `Unset` and `1` means the OperationState is `Done`. + +#### [](#TimelockControllerComponent-get_operation_state)`get_operation_state(self: @ContractState, id: felt252) → OperationState` external + +Returns the current state of the operation with the given `id`. + +The possible states are: + +- `Unset`: the operation has not been scheduled or has been canceled. +- `Waiting`: the operation has been scheduled and is pending the scheduled delay. +- `Ready`: the timer has expired, and the operation is eligible for execution. +- `Done`: the operation has been executed. + +#### [](#TimelockControllerComponent-get_min_delay)`get_min_delay(self: @ContractState) → u64` external + +Returns the minimum delay in seconds for an operation to become valid. This value can be changed by executing an operation that calls `update_delay`. + +#### [](#TimelockControllerComponent-hash_operation)`hash_operation(self: @ContractState, call: Call, predecessor: felt252, salt: felt252)` external + +Returns the identifier of an operation containing a single transaction. + +#### [](#TimelockControllerComponent-hash_operation_batch)`hash_operation_batch(self: @ContractState, calls: Span, predecessor: felt252, salt: felt252)` external + +Returns the identifier of an operation containing a batch of transactions. + +#### [](#TimelockControllerComponent-schedule)`schedule(ref self: ContractState, call: Call, predecessor: felt252, salt: felt252, delay: u64)` external + +Schedule an operation containing a single transaction. + +Requirements: + +- The caller must have the `PROPOSER_ROLE` role. +- The proposal must not already exist. +- `delay` must be greater than or equal to the min delay. + +Emits [CallScheduled](#TimelockControllerComponent-CallScheduled) event. Emits [CallSalt](#TimelockControllerComponent-CallSalt) event if `salt` is not zero. + +#### [](#TimelockControllerComponent-schedule_batch)`schedule_batch(ref self: ContractState, calls: Span, predecessor: felt252, salt: felt252, delay: u64)` external + +Schedule an operation containing a batch of transactions. + +Requirements: + +- The caller must have the `PROPOSER_ROLE` role. +- The proposal must not already exist. +- `delay` must be greater than or equal to the min delay. + +Emits one [CallScheduled](#TimelockControllerComponent-CallScheduled) event for each transaction in the batch. Emits [CallSalt](#TimelockControllerComponent-CallSalt) event if `salt` is not zero. + +#### [](#TimelockControllerComponent-cancel)`cancel(ref self: ContractState, id: felt252)` external + +Cancels an operation. A canceled operation returns to `Unset` OperationState. + +Requirements: + +- The caller must have the `CANCELLER_ROLE` role. +- `id` must be a pending operation. + +Emits a [CallCancelled](#TimelockControllerComponent-CallCancelled) event. + +#### [](#TimelockControllerComponent-execute)`execute(ref self: ContractState, call: Call, predecessor: felt252, salt: felt252)` external + +Execute a (Ready) operation containing a single Call. + +Requirements: + +- Caller must have `EXECUTOR_ROLE`. +- `id` must be in Ready OperationState. +- `predecessor` must either be `0` or in Done OperationState. + +Emits a [CallExecuted](#TimelockControllerComponent-CallExecuted) event. + +This function can reenter, but it doesn’t pose a risk because [`_after_call(self: @ContractState, id: felt252)` internal](#TimelockControllerComponent-_after_call) checks that the proposal is pending, thus any modifications to the operation during reentrancy should be caught. + +#### [](#TimelockControllerComponent-execute_batch)`execute_batch(ref self: ContractState, calls: Span, predecessor: felt252, salt: felt252)` external + +Execute a (Ready) operation containing a batch of Calls. + +Requirements: + +- Caller must have `EXECUTOR_ROLE`. +- `id` must be in Ready OperationState. +- `predecessor` must either be `0` or in Done OperationState. + +Emits a [CallExecuted](#TimelockControllerComponent-CallExecuted) event for each Call. + +This function can reenter, but it doesn’t pose a risk because `_after_call` checks that the proposal is pending, thus any modifications to the operation during reentrancy should be caught. + +#### [](#TimelockControllerComponent-update_delay)`update_delay(ref self: ContractState, new_delay: u64)` external + +Changes the minimum timelock duration for future operations. + +Requirements: + +- The caller must be the timelock itself. This can only be achieved by scheduling and later executing an operation where the timelock is the target and the data is the serialized call to this function. + +Emits a [MinDelayChanged](#TimelockControllerComponent-MinDelayChanged) event. + +#### [](#TimelockControllerComponent-Internal-Functions)Internal functions + +#### [](#TimelockControllerComponent-initializer)`initializer(ref self: ContractState, min_delay: u64, proposers: Span, executors: Span, admin: ContractAddress)` internal + +Initializes the contract by registering support for SRC5 and AccessControl. + +This function also configures the contract with the following parameters: + +- `min_delay`: initial minimum delay in seconds for operations. +- `proposers`: accounts to be granted proposer and canceller roles. +- `executors`: accounts to be granted executor role. +- `admin`: optional account to be granted admin role; disable with zero address. + +The optional admin can aid with initial configuration of roles after deployment without being subject to delay, but this role should be subsequently renounced in favor of administration through timelocked proposals. + +Emits two [IAccessControl::RoleGranted](access#IAccessControl-RoleGranted) events for each account in `proposers` with `PROPOSER_ROLE` and `CANCELLER_ROLE` roles. + +Emits a [IAccessControl::RoleGranted](access#IAccessControl-RoleGranted) event for each account in `executors` with `EXECUTOR_ROLE` role. + +May emit a [IAccessControl::RoleGranted](access#IAccessControl-RoleGranted) event for `admin` with `DEFAULT_ADMIN_ROLE` role (if `admin` is not zero). + +Emits [MinDelayChanged](#TimelockControllerComponent-MinDelayChanged) event. + +#### [](#TimelockControllerComponent-assert_only_role)`assert_only_role(self: @ContractState, role: felt252)` internal + +Validates that the caller has the given `role`. Otherwise it panics. + +#### [](#TimelockControllerComponent-assert_only_role_or_open_role)`assert_only_role_or_open_role(self: @ContractState, role: felt252)` internal + +Validates that the caller has the given `role`. If `role` is granted to the zero address, then this is considered an open role which allows anyone to be the caller. + +#### [](#TimelockControllerComponent-assert_only_self)`assert_only_self(self: @ContractState)` internal + +Validates that the caller is the timelock contract itself. Otherwise it panics. + +#### [](#TimelockControllerComponent-_before_call)`_before_call(self: @ContractState, id: felt252, predecessor: felt252)` internal + +Private function that checks before execution of an operation’s calls. + +Requirements: + +- `id` must be in the `Ready` OperationState. +- `predecessor` must either be zero or be in the `Done` OperationState. + +#### [](#TimelockControllerComponent-_after_call)`_after_call(self: @ContractState, id: felt252)` internal + +Private function that checks after execution of an operation’s calls and sets the OperationState of `id` to `Done`. + +Requirements: + +- `id` must be in the Ready OperationState. + +#### [](#TimelockControllerComponent-_schedule)`_schedule(ref self: ContractState, id: felt252, delay: u64)` internal + +Private function that schedules an operation that is to become valid after a given `delay`. + +#### [](#TimelockControllerComponent-_execute)`_execute(ref self: ContractState, call: Call)` internal + +Private function that executes an operation’s calls. + +#### [](#TimelockControllerComponent-Events)Events + +#### [](#TimelockControllerComponent-CallScheduled)`CallScheduled(id: felt252, index: felt252, call: Call, predecessor: felt252, delay: u64)` event + +Emitted when `call` is scheduled as part of operation `id`. + +#### [](#TimelockControllerComponent-CallExecuted)`CallExecuted(id: felt252, index: felt252, call: Call)` event + +Emitted when `call` is performed as part of operation `id`. + +#### [](#TimelockControllerComponent-CallSalt)`CallSalt(id: felt252, salt: felt252)` event + +Emitted when a new proposal is scheduled with non-zero salt. + +#### [](#TimelockControllerComponent-CallCancelled)`CallCancelled(id: felt252)` event + +Emitted when operation `id` is cancelled. + +#### [](#TimelockControllerComponent-MinDelayChanged)`MinDelayChanged(old_duration: u64, new_duration: u64)` event + +Emitted when the minimum delay for future operations is modified. + +## [](#votes)Votes + +The `VotesComponent` provides a flexible system for tracking and delegating voting power. This system allows users to delegate their voting power to other addresses, enabling more active participation in governance. + +### [](#VotesComponent)`VotesComponent`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/governance/src/votes/votes.cairo) + +```cairo +use openzeppelin_governance::votes::VotesComponent; +``` + +Component that implements the [IVotes](#IVotes) interface and provides a flexible system for tracking and delegating voting power. + +By default, token balance does not account for voting power. This makes transfers cheaper. The downside is that it requires users to delegate to themselves in order to activate checkpoints and have their voting power tracked. + +When using this module, your contract must implement the [VotingUnitsTrait](#VotingUnitsTrait). For convenience, this is done automatically for `ERC20` and `ERC721` tokens. + +Voting Units Trait Implementations + +ERC20VotesImpl + +- [`get_voting_units(self, account)`](#VotesComponent-ERC20VotesImpl-get_voting_units) + +ERC721VotesImpl + +- [`get_voting_units(self, account)`](#VotesComponent-ERC721VotesImpl-get_voting_units) + +Embeddable Implementations + +VotesImpl + +- [`get_votes(self, account)`](#VotesComponent-get_votes) +- [`get_past_votes(self, account, timepoint)`](#VotesComponent-get_past_votes) +- [`get_past_total_supply(self, timepoint)`](#VotesComponent-get_past_total_supply) +- [`delegates(self, account)`](#VotesComponent-delegates) +- [`delegate(self, delegatee)`](#VotesComponent-delegate) +- [`delegate_by_sig(self, delegator, delegatee, nonce, expiry, signature)`](#VotesComponent-delegate_by_sig) +- [`clock(self)`](#VotesComponent-clock) +- [`CLOCK_MODE(self)`](#VotesComponent-CLOCK_MODE) + +Internal implementations + +InternalImpl + +- [`get_total_supply(self)`](#VotesComponent-get_total_supply) +- [`move_delegate_votes(self, from, to, amount)`](#VotesComponent-move_delegate_votes) +- [`transfer_voting_units(self, from, to, amount)`](#VotesComponent-transfer_voting_units) +- [`num_checkpoints(self, account)`](#VotesComponent-num_checkpoints) +- [`checkpoints(self, account, pos)`](#VotesComponent-checkpoints) +- [`_delegate(self, account, delegatee)`](#VotesComponent-_delegate) + +Events + +- [`DelegateChanged(delegator, from_delegate, to_delegate)`](#VotesComponent-DelegateChanged) +- [`DelegateVotesChanged(delegate, previous_votes, new_votes)`](#VotesComponent-DelegateVotesChanged) + +#### [](#VotesComponent-ERC20VotesImpl)ERC20VotesImpl + +#### [](#VotesComponent-ERC20VotesImpl-get_voting_units)`get_voting_units(self: @ContractState, account: ContractAddress) → u256` internal + +Returns the number of voting units for a given account. + +This implementation is specific to ERC20 tokens, where the balance of tokens directly represents the number of voting units. + +This implementation will work out of the box if the ERC20 component is implemented in the final contract. + +This implementation assumes tokens map to voting units 1:1. Any deviation from this formula when transferring voting units (e.g. by using hooks) may compromise the internal vote accounting. + +#### [](#VotesComponent-ERC721VotesImpl)ERC721VotesImpl + +#### [](#VotesComponent-ERC721VotesImpl-get_voting_units)`get_voting_units(self: @ContractState, account: ContractAddress) → u256` internal + +Returns the number of voting units for a given account. + +This implementation is specific to ERC721 tokens, where each token represents one voting unit. The function returns the balance of ERC721 tokens for the specified account. + +This implementation will work out of the box if the ERC721 component is implemented in the final contract. + +This implementation assumes tokens map to voting units 1:1. Any deviation from this formula when transferring voting units (e.g. by using hooks) may compromise the internal vote accounting. + +#### [](#VotesComponent-Functions)Embeddable functions + +#### [](#VotesComponent-get_votes)`get_votes(self: @ContractState, account: ContractAddress) → u256` external + +Returns the current amount of votes that `account` has. + +#### [](#VotesComponent-get_past_votes)`get_past_votes(self: @ContractState, account: ContractAddress, timepoint: u64) → u256` external + +Returns the amount of votes that `account` had at a specific moment in the past. + +Requirements: + +- `timepoint` must be in the past. + +#### [](#VotesComponent-get_past_total_supply)`get_past_total_supply(self: @ContractState, timepoint: u64) → u256` external + +Returns the total supply of votes available at a specific moment in the past. + +This value is the sum of all available votes, which is not necessarily the sum of all delegated votes. Votes that have not been delegated are still part of total supply, even though they would not participate in a vote. + +Requirements: + +- `timepoint` must be in the past. + +#### [](#VotesComponent-delegates)`delegates(self: @ContractState, account: ContractAddress) → ContractAddress` external + +Returns the delegate that `account` has chosen. + +#### [](#VotesComponent-delegate)`delegate(ref self: ContractState, delegatee: ContractAddress)` external + +Delegates votes from the sender to `delegatee`. + +Emits a [DelegateChanged](#VotesComponent-DelegateChanged) event. + +May emit one or two [DelegateVotesChanged](#VotesComponent-DelegateVotesChanged) events. + +#### [](#VotesComponent-delegate_by_sig)`delegate_by_sig(ref self: ContractState, delegator: ContractAddress, delegatee: ContractAddress, nonce: felt252, expiry: u64, signature: Span)` external + +Delegates votes from `delegator` to `delegatee` through a [SNIP-12](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-12.md) message signature validation. + +Requirements: + +- `expiry` must not be in the past. +- `nonce` must match the account’s current nonce. +- `delegator` must implement `SRC6::is_valid_signature`. +- `signature` should be valid for the message hash. + +Emits a [DelegateChanged](#VotesComponent-DelegateChanged) event. + +May emit one or two [DelegateVotesChanged](#VotesComponent-DelegateVotesChanged) events. + +#### [](#VotesComponent-clock)`clock(self: @ContractState) → u64` external + +Returns the current timepoint determined by the contract’s operational mode, intended for use in time-sensitive logic. See [ERC-6372#clock](https://eips.ethereum.org/EIPS/eip-6372#clock). + +Requirements: + +- This function MUST always be non-decreasing. + +#### [](#VotesComponent-CLOCK_MODE)`CLOCK_MODE(self: @ContractState) → u64` external + +Returns a description of the clock the contract is operating in. See [ERC-6372#CLOCK\_MODE](https://eips.ethereum.org/EIPS/eip-6372#clock_mode). + +Requirements: + +- The output MUST be formatted like a URL query string, decodable in standard JavaScript. + +#### [](#VotesComponent-Internal-functions)Internal functions + +#### [](#VotesComponent-get_total_supply)`get_total_supply(self: @ContractState) → u256` internal + +Returns the current total supply of votes. + +#### [](#VotesComponent-move_delegate_votes)`move_delegate_votes(ref self: ContractState, from: ContractAddress, to: ContractAddress, amount: u256)` internal + +Moves delegated votes from one delegate to another. + +May emit one or two [DelegateVotesChanged](#VotesComponent-DelegateVotesChanged) events. + +#### [](#VotesComponent-transfer_voting_units)`transfer_voting_units(ref self: ContractState, from: ContractAddress, to: ContractAddress, amount: u256)` internal + +Transfers, mints, or burns voting units. + +To register a mint, `from` should be zero. To register a burn, `to` should be zero. Total supply of voting units will be adjusted with mints and burns. + +If voting units are based on an underlying transferable asset (like a token), you must call this function every time the asset is transferred to keep the internal voting power accounting in sync. For ERC20 and ERC721 tokens, this is typically handled using hooks. + +May emit one or two [DelegateVotesChanged](#VotesComponent-DelegateVotesChanged) events. + +#### [](#VotesComponent-num_checkpoints)`num_checkpoints(self: @ContractState, account: ContractAddress) → u64` internal + +Returns the number of checkpoints for `account`. + +#### [](#VotesComponent-checkpoints)`checkpoints(self: @ContractState, account: ContractAddress, pos: u64) → Checkpoint` internal + +Returns the `pos`-th checkpoint for `account`. + +#### [](#VotesComponent-_delegate)`_delegate(ref self: ContractState, account: ContractAddress, delegatee: ContractAddress)` internal + +Delegates all of `account`'s voting units to `delegatee`. + +Emits a [DelegateChanged](#VotesComponent-DelegateChanged) event. + +May emit one or two [DelegateVotesChanged](#VotesComponent-DelegateVotesChanged) events. + +#### [](#VotesComponent-Events)Events + +#### [](#VotesComponent-DelegateChanged)`DelegateChanged(delegator: ContractAddress, from_delegate: ContractAddress, to_delegate: ContractAddress)` event + +Emitted when an account changes their delegate. + +#### [](#VotesComponent-DelegateVotesChanged)`DelegateVotesChanged(delegate: ContractAddress, previous_votes: u256, new_votes: u256)` event + +Emitted when a token transfer or delegate change results in changes to a delegate’s number of votes. + +### [](#VotingUnitsTrait)`VotingUnitsTrait`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/governance/src/votes/votes.cairo) + +```cairo +pub trait VotingUnitsTrait { + fn get_voting_units(self: @TState, account: ContractAddress) -> u256; +} +``` + +A trait that must be implemented when integrating [VotesComponent](#VotesComponent) into a contract. It offers a mechanism to retrieve the number of voting units for a given account at the current time. + +Functions + +- [`get_voting_units(self, account)`](#VotingUnitsTrait-get_voting_units) + +#### [](#VotingUnitsTrait-Functions)Functions + +#### [](#VotingUnitsTrait-get_voting_units)`get_voting_units(self: @TState, account: ContractAddress) → u256` external + +Returns the number of voting units for a given account. For ERC20, this is typically the token balance. For ERC721, this is typically the number of tokens owned. + +While any formula can be used as a measure of voting units, the internal vote accounting of the contract may be compromised if voting units are transferred in any external flow by following a different formula. +For example, when implementing the hook for ERC20, the number of voting units transferred should match the formula given by the `get_voting_units` implementation. diff --git a/content/cairo-contracts/api/introspection.mdx b/content/cairo-contracts/api/introspection.mdx new file mode 100644 index 00000000..8656e61c --- /dev/null +++ b/content/cairo-contracts/api/introspection.mdx @@ -0,0 +1,72 @@ +--- +title: Introspection +--- + +This crate handles [type introspection](https://en.wikipedia.org/wiki/Type_introspection) of contracts. In other words, it examines which functions can be called on a given contract. This is referred to as the contract’s interface. + +## [](#interfaces)Interfaces + +Starting from version `3.x.x`, the interfaces are no longer part of the `openzeppelin_introspection` package. The references documented here are contained in the `openzeppelin_interfaces` package version `v2.1.0`. + +### [](#ISRC5)`ISRC5`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/interfaces/src/introspection.cairo) + +```cairo +use openzeppelin_interfaces::introspection::ISRC5; +``` + +Interface of the SRC5 Introspection Standard as defined in [SNIP-5](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-5.md). + +[SRC5 ID](#ISRC5) + +0x3f918d17e5ee77373b56385708f855659a07f75997f365cf87748628532a055 + +Functions + +- [`supports_interface(interface_id)`](#ISRC5-supports_interface) + +#### [](#ISRC5-Functions)Functions + +#### [](#ISRC5-supports_interface)`supports_interface(interface_id: felt252) → bool` external + +Checks whether the contract implements the given interface. + +Check [Computing the Interface ID](../introspection#computing_the_interface_id) for more information on how to compute this ID. + +## [](#core)Core + +### [](#SRC5Component)`SRC5Component`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/introspection/src/src5.cairo) + +```cairo +use openzeppelin_introspection::src5::SRC5Component; +``` + +SRC5 component extending [`ISRC5`](#ISRC5). + +Embeddable Implementations + +SRC5Impl + +- [`supports_interface(self, interface_id)`](#SRC5Component-supports_interface) + +Internal Implementations + +InternalImpl + +- [`register_interface(self, interface_id)`](#SRC5Component-register_interface) +- [`deregister_interface(self, interface_id)`](#SRC5Component-deregister_interface) + +#### [](#SRC5Component-Embeddable-Functions)Embeddable functions + +#### [](#SRC5Component-supports_interface)`supports_interface(self: @ContractState, interface_id: felt252) → bool` external + +See [`ISRC5::supports_interface`](#ISRC5-supports_interface). + +#### [](#SRC5Component-Internal-Functions)Internal functions + +#### [](#SRC5Component-register_interface)`register_interface(ref self: ComponentState, interface_id: felt252)` internal + +Registers support for the given `interface_id`. + +#### [](#SRC5Component-deregister_interface)`deregister_interface(ref self: ComponentState, interface_id: felt252)` internal + +Deregisters support for the given `interface_id`. diff --git a/content/cairo-contracts/api/merkle-tree.mdx b/content/cairo-contracts/api/merkle-tree.mdx new file mode 100644 index 00000000..a1e7a660 --- /dev/null +++ b/content/cairo-contracts/api/merkle-tree.mdx @@ -0,0 +1,143 @@ +--- +title: Merkle Tree +--- + +This crate provides a set of utilities for verifying Merkle Tree proofs on-chain. The tree and the proofs can be generated using this [JavaScript library](https://github.com/ericnordelo/strk-merkle-tree). + +This module provides: + +- `verify` - can prove that some value is part of a Merkle tree. +- `verify_multi_proof` - can prove multiple values are part of a Merkle tree. + +`openzeppelin_merkle_tree` doesn’t have dependencies outside of `corelib`, and can be used in projects that are not Starknet-related. + +To use it as a standalone package, you can add it in your `Scarb.toml` as follows: + +`openzeppelin_merkle_tree = "3.0.0-alpha.1"` + +## [](#modules)Modules + +### [](#merkle_proof)`merkle_proof`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/merkle_tree/src/merkle_proof.cairo) + +```cairo +use openzeppelin_merkle_tree::merkle_proof; +``` + +These functions deal with verification of Merkle Tree proofs. + +The tree and the proofs can be generated using this [JavaScript library](https://github.com/ericnordelo/strk-merkle-tree). You will find a quickstart guide in the readme. + +You should avoid using leaf values that are two felt252 values long prior to hashing, or use a hash function other than the one used to hash internal nodes for hashing leaves. This is because the concatenation of a sorted pair of internal nodes in the Merkle tree could be reinterpreted as a leaf value. The JavaScript library generates Merkle trees that are safe against this attack out of the box. + +Functions + +- [`verify(proof, root, leaf)`](#merkle_proof-verify) +- [`verify_pedersen(proof, root, leaf)`](#merkle_proof-verify_pedersen) +- [`verify_poseidon(proof, root, leaf)`](#merkle_proof-verify_poseidon) +- [`process_proof(proof, leaf)`](#merkle_proof-process_proof) +- [`verify_multi_proof(proof, proof_flags, root, leaves)`](#merkle_proof-verify_multi_proof) +- [`process_multi_proof(proof, proof_flags, leaf)`](#merkle_proof-process_multi_proof) + +#### [](#merkle_proof-Functions)Functions + +#### [](#merkle_proof-verify)`verify<+CommutativeHasher>(proof: Span, root: felt252, leaf: felt252) → bool` public + +Returns true if a `leaf` can be proved to be a part of a Merkle tree defined by `root`. + +For this, a `proof` must be provided, containing sibling hashes on the branch from the leaf to the root of the tree. + +Each pair of leaves and each pair of pre-images are assumed to be sorted. + +This function expects a `CommutativeHasher` implementation. See [hashes::CommutativeHasher](#hashes-CommutativeHasher) for more information. + +`verify_pedersen` and `verify_poseidon` already include the corresponding `Hasher` implementations. + +#### [](#merkle_proof-verify_pedersen)`verify_pedersen(proof: Span, root: felt252, leaf: felt252) → bool` public + +Version of `verify` using Pedersen as the hashing function. + +#### [](#merkle_proof-verify_poseidon)`verify_poseidon(proof: Span, root: felt252, leaf: felt252) → bool` public + +Version of `verify` using Poseidon as the hashing function. + +#### [](#merkle_proof-process_proof)`process_proof<+CommutativeHasher>(proof: Span, leaf: felt252) → felt252` public + +Returns the rebuilt hash obtained by traversing a Merkle tree up from `leaf` using `proof`. + +A `proof` is valid if and only if the rebuilt hash matches the root of the tree. + +When processing the proof, the pairs of leaves & pre-images are assumed to be sorted. + +This function expects a `CommutativeHasher` implementation. See [hashes::CommutativeHasher](#hashes-CommutativeHasher) for more information. + +#### [](#merkle_proof-verify_multi_proof)`verify_multi_proof<+CommutativeHasher>(proof: Span, proof_flags: Span, root: felt252, leaves: Span) → bool` public + +Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by `root`, according to `proof` and `proof_flags` as described in `process_multi_proof`. + +The `leaves` must be validated independently. + +Not all Merkle trees admit multiproofs. See `process_multi_proof` for details. + +Consider the case where `root == proof.at(0) && leaves.len() == 0` as it will return `true`. + +This function expects a `CommutativeHasher` implementation. See [hashes::CommutativeHasher](#hashes-CommutativeHasher) for more information. + +#### [](#merkle_proof-process_multi_proof)`process_multi_proof<+CommutativeHasher>(proof: Span, proof_flags: Span, leaves: Span) → felt252` public + +Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. + +The reconstruction proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another leaf/inner node or a proof sibling node, depending on whether each `proof_flags` item is true or false respectively. + +Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: + +1. The tree is complete (but not necessarily perfect). +2. The leaves to be proven are in the opposite order than they are in the tree. (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer). + +The *empty set* (i.e. the case where `proof.len() == 1 && leaves.len() == 0`) is considered a no-op, and therefore a valid multiproof (i.e. it returns `proof.at(0)`). Consider disallowing this case if you’re not validating the leaves elsewhere. + +This function expects a `CommutativeHasher` implementation. See [hashes::CommutativeHasher](#hashes-CommutativeHasher) for more information. + +### [](#hashes)`hashes`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/merkle_tree/src/hashes.cairo) + +```cairo +use openzeppelin_merkle_tree::hashes; +``` + +Module providing the trait and default implementations for the commutative hash functions used in [`merkle_proof`](#merkle_proof). + +The `PedersenCHasher` implementation matches the default node hashing function used in the [JavaScript library](https://github.com/ericnordelo/strk-merkle-tree). + +Traits + +- [`CommutativeHasher`](#hashes-CommutativeHasher) + +Impls + +- [`PedersenCHasher`](#hashes-PedersenCHasher) +- [`PoseidonCHasher`](#hashes-PoseidonCHasher) + +#### [](#hashes-Traits)Traits + +#### [](#hashes-CommutativeHasher)`CommutativeHasher` trait + +Declares a commutative hash function with the following signature: + +`commutative_hash(a: felt252, b: felt252) → felt252;` + +which computes a commutative hash of a sorted pair of felt252 values. + +This is usually implemented as an extension of a non-commutative hash function, like Pedersen or Poseidon, returning the hash of the concatenation of the two values by first sorting them. + +Frequently used when working with merkle proofs. + +The `commutative_hash` function MUST follow the invariant that `commutative_hash(a, b) == commutative_hash(b, a)`. + +#### [](#hashes-Impls)Impls + +#### [](#hashes-PedersenCHasher)`PedersenCHasher` impl + +Implementation of the `CommutativeHasher` trait which computes the Pedersen hash of chaining the two input values with the len (2), sorting the pair first. + +#### [](#hashes-PoseidonCHasher)`PoseidonCHasher` impl + +Implementation of the `CommutativeHasher` trait which computes the Poseidon hash of the concatenation of two values, sorting the pair first. diff --git a/content/cairo-contracts/api/security.mdx b/content/cairo-contracts/api/security.mdx new file mode 100644 index 00000000..31b88f2a --- /dev/null +++ b/content/cairo-contracts/api/security.mdx @@ -0,0 +1,150 @@ +--- +title: Security +--- + +This crate provides components to handle common security-related tasks. + +## [](#initializable)Initializable + +### [](#InitializableComponent)`InitializableComponent`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/security/src/initializable.cairo) + +```cairo +use openzeppelin_security::InitializableComponent; +``` + +Component enabling one-time initialization for contracts. + +Embeddable Implementations + +InitializableImpl + +- [`is_initialized(self)`](#InitializableComponent-is_initialized) + +Internal Implementations + +InternalImpl + +- [`initialize(self)`](#InitializableComponent-initialize) + +#### [](#InitializableComponent-Embeddable-Functions)Embeddable functions + +#### [](#InitializableComponent-is_initialized)`is_initialized(self: @ComponentState) → bool` external + +Returns whether the contract has been initialized. + +#### [](#InitializableComponent-Internal-Functions)Internal functions + +#### [](#InitializableComponent-initialize)`initialize(ref self: ComponentState)` internal + +Initializes the contract. Can only be called once. + +Requirements: + +- the contract must not have been initialized before. + +## [](#pausable)Pausable + +### [](#PausableComponent)`PausableComponent`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/security/src/pausable.cairo) + +```cairo +use openzeppelin_security::PausableComponent; +``` + +Component to implement an emergency stop mechanism. + +Embeddable Implementations + +PausableImpl + +- [`is_paused(self)`](#PausableComponent-is_paused) + +Internal Implementations + +InternalImpl + +- [`assert_not_paused(self)`](#PausableComponent-assert_not_paused) +- [`assert_paused(self)`](#PausableComponent-assert_paused) +- [`pause(self)`](#PausableComponent-pause) +- [`unpause(self)`](#PausableComponent-unpause) + +Events + +- [`Paused(account)`](#PausableComponent-Paused) +- [`Unpaused(account)`](#PausableComponent-Unpaused) + +#### [](#PausableComponent-Embeddable-Functions)Embeddable functions + +#### [](#PausableComponent-is_paused)`is_paused(self: @ComponentState) → bool` external + +Returns whether the contract is currently paused. + +#### [](#PausableComponent-Internal-Functions)Internal functions + +#### [](#PausableComponent-assert_not_paused)`assert_not_paused(self: @ComponentState)` internal + +Panics if the contract is paused. + +#### [](#PausableComponent-assert_paused)`assert_paused(self: @ComponentState)` internal + +Panics if the contract is not paused. + +#### [](#PausableComponent-pause)`pause(ref self: ComponentState)` internal + +Pauses the contract. + +Requirements: + +- the contract must not be paused. + +Emits a [Paused](#PausableComponent-Paused) event. + +#### [](#PausableComponent-unpause)`unpause(ref self: ComponentState)` internal + +Unpauses the contract. + +Requirements: + +- the contract must be paused. + +Emits an [Unpaused](#PausableComponent-Unpaused) event. + +#### [](#PausableComponent-Events)Events + +#### [](#PausableComponent-Paused)`Paused(account: ContractAddress)` event + +Emitted when the contract is paused by `account`. + +#### [](#PausableComponent-Unpaused)`Unpaused(account: ContractAddress)` event + +Emitted when the contract is unpaused by `account`. + +## [](#reentrancyguard)ReentrancyGuard + +### [](#ReentrancyGuardComponent)`ReentrancyGuardComponent`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/security/src/reentrancyguard.cairo) + +```cairo +use openzeppelin_security::ReentrancyGuardComponent; +``` + +Component to help prevent reentrant calls. + +Internal Implementations + +InternalImpl + +- [`start(self)`](#ReentrancyGuardComponent-start) +- [`end(self)`](#ReentrancyGuardComponent-end) + +#### [](#ReentrancyGuardComponent-Internal-Functions)Internal functions + +#### [](#ReentrancyGuardComponent-start)`start(ref self: ComponentState)` internal + +Prevents a contract’s function from calling itself or another protected function, directly or indirectly. + +Requirements: + +- the guard must not be currently enabled. + +#### [](#ReentrancyGuardComponent-end)`end(ref self: ComponentState)` internal + +Removes the reentrant guard. diff --git a/content/cairo-contracts/api/token_common.mdx b/content/cairo-contracts/api/token_common.mdx new file mode 100644 index 00000000..8ebbf62e --- /dev/null +++ b/content/cairo-contracts/api/token_common.mdx @@ -0,0 +1,347 @@ +--- +title: Common (Token) +--- + +This module provides extensions and utilities that are common to multiple token standards. + +## [](#interfaces)Interfaces + +Starting from version `3.x.x`, the interfaces are no longer part of the `openzeppelin_token` package. The references documented here are contained in the `openzeppelin_interfaces` package version `v2.1.0`. + +### [](#IERC2981)`IERC2981`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/interfaces/src/token/erc2981.cairo) + +```cairo +use openzeppelin_interfaces::erc2981::IERC2981; +``` + +[SRC5 ID](introspection#ISRC5) + +0x2d3414e45a8700c29f119a54b9f11dca0e29e06ddcb214018fc37340e165ed6 + +Interface of the ERC2981 standard as defined in [EIP-2981](https://eips.ethereum.org/EIPS/eip-2981). + +Functions + +- [`royalty_info(token_id, sale_price)`](#IERC2981-royalty_info) + +#### [](#IERC2981-Functions)Functions + +#### [](#IERC2981-royalty_info)`royalty_info(token_id: u256, sale_price: u256) → (ContractAddress, u256)` external + +Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of exchange. The royalty amount is denominated and must be paid in that same unit of exchange. + +### [](#IERC2981Info)`IERC2981Info`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/interfaces/src/token/erc2981.cairo) + +```cairo +use openzeppelin_interfaces::erc2981::IERC2981Info; +``` + +Interface providing external read functions for discovering the state of ERC2981 component. + +Functions + +- [`default_royalty()`](#IERC2981Info-default_royalty) +- [`token_royalty(token_id)`](#IERC2981Info-token_royalty) + +#### [](#IERC2981Info-Functions)Functions + +#### [](#IERC2981Info-default_royalty)`default_royalty() → (ContractAddress, u128, u128)` external + +Returns the royalty information that all ids in this contract will default to. + +The returned tuple contains: + +- `t.0`: The receiver of the royalty payment. +- `t.1`: The numerator of the royalty fraction. +- `t.2`: The denominator of the royalty fraction. + +#### [](#IERC2981Info-token_royalty)`token_royalty(token_id: u256) → (ContractAddress, u128, u128)` external + +Returns the royalty information specific to a token. + +The returned tuple contains: + +- `t.0`: The receiver of the royalty payment. +- `t.1`: The numerator of the royalty fraction. +- `t.2`: The denominator of the royalty fraction. + +### [](#IERC2981Admin)`IERC2981Admin`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/interfaces/src/token/erc2981.cairo) + +```cairo +use openzeppelin_interfaces::erc2981::IERC2981Admin; +``` + +Interface providing external admin functions for managing the settings of ERC2981 component. + +Functions + +- [`set_default_royalty(receiver, fee_numerator)`](#IERC2981Admin-set_default_royalty) +- [`delete_default_royalty()`](#IERC2981Admin-delete_default_royalty) +- [`set_token_royalty(token_id, receiver, fee_numerator)`](#IERC2981Admin-set_token_royalty) +- [`reset_token_royalty(token_id)`](#IERC2981Admin-reset_token_royalty) + +#### [](#IERC2981Admin-Functions)Functions + +#### [](#IERC2981Admin-set_default_royalty)`set_default_royalty(receiver: ContractAddress, fee_numerator: u128)` external + +Sets the royalty information that all ids in this contract will default to. + +#### [](#IERC2981Admin-delete_default_royalty)`delete_default_royalty()` external + +Sets the default royalty percentage and receiver to zero. + +#### [](#IERC2981Admin-set_token_royalty)`set_token_royalty(token_id: u256, receiver: ContractAddress, fee_numerator: u128)` external + +Sets the royalty information for a specific token id that takes precedence over the global default. + +#### [](#IERC2981Admin-reset_token_royalty)`reset_token_royalty(token_id: u256)` external + +Resets royalty information for the token id back to unset. + +## [](#erc2981)ERC2981 + +### [](#ERC2981Component)`ERC2981Component`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/token/src/common/erc2981/erc2981.cairo) + +```cairo +use openzeppelin_token::common::erc2981::ERC2981Component; +``` + +ERC2981 component extending [IERC2981](#IERC2981). + +[Immutable Component Config](../components#immutable_config) + +constants + +- [`FEE_DENOMINATOR`](#ERC2981Component-IC-FEE_DENOMINATOR) + +functions + +- [`validate()`](#ERC2981Component-IC-validate) + +Embeddable Implementations + +ERC2981Impl + +- [`royalty_info(self, token_id, sale_price)`](#ERC2981Component-royalty_info) + +ERC2981InfoImpl + +- [`default_royalty(self)`](#ERC2981InfoImpl-default_royalty) +- [`token_royalty(self, token_id)`](#ERC2981InfoImpl-token_royalty) + +ERC2981AdminOwnableImpl + +- [`set_default_royalty(self, receiver, fee_numerator)`](#ERC2981AdminOwnableImpl-set_default_royalty) +- [`delete_default_royalty(self)`](#ERC2981AdminOwnableImpl-delete_default_royalty) +- [`set_token_royalty(self, token_id, receiver, fee_numerator)`](#ERC2981AdminOwnableImpl-set_token_royalty) +- [`reset_token_royalty(self, token_id)`](#ERC2981AdminOwnableImpl-reset_token_royalty) + +ERC2981AdminAccessControlImpl + +- [`set_default_royalty(self, receiver, fee_numerator)`](#ERC2981AdminAccessControlImpl-set_default_royalty) +- [`delete_default_royalty(self)`](#ERC2981AdminAccessControlImpl-delete_default_royalty) +- [`set_token_royalty(self, token_id, receiver, fee_numerator)`](#ERC2981AdminAccessControlImpl-set_token_royalty) +- [`reset_token_royalty(self, token_id)`](#ERC2981AdminAccessControlImpl-reset_token_royalty) + +Internal implementations + +InternalImpl + +- [`initializer(self, default_receiver, default_royalty_fraction)`](#ERC2981Component-initializer) +- [`_default_royalty(self)`](#ERC2981Component-_default_royalty) +- [`_set_default_royalty(self, receiver, fee_numerator)`](#ERC2981Component-_set_default_royalty) +- [`_delete_default_royalty(self)`](#ERC2981Component-_delete_default_royalty) +- [`_token_royalty(self, token_id)`](#ERC2981Component-_token_royalty) +- [`_set_token_royalty(self, token_id, receiver, fee_numerator)`](#ERC2981Component-_set_token_royalty) +- [`_reset_token_royalty(self, token_id)`](#ERC2981Component-_reset_token_royalty) + +#### [](#ERC2981Component-Immutable-Config)Immutable Config constants + +#### [](#ERC2981Component-IC-FEE_DENOMINATOR)`FEE_DENOMINATOR: u128` constant + +The denominator with which to interpret the fee set in `_set_token_royalty` and `_set_default_royalty` as a fraction of the sale price. + +#### [](#ERC2981Component-IC-validate)`validate()` internal + +Validates the given implementation of the contract’s configuration. + +Requirements: + +- `FEE_DENOMINATOR` must be greater than 0. + +This function is called by the contract’s initializer. + +#### [](#ERC2981Component-Embeddable-functions)Embeddable functions + +#### [](#ERC2981Component-royalty_info)`royalty_info(@self: ContractState, token_id: u256, sale_price: u256) → (ContractAddress, u256)` external + +Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of exchange. The royalty amount is denominated and should be paid in that same unit of exchange. + +The returned tuple contains: + +- `t.0`: The receiver of the royalty payment. +- `t.1`: The amount of royalty payment. + +#### [](#ERC2981InfoImpl-default_royalty)`default_royalty(@self: ContractState) → (ContractAddress, u128, u128)` external + +Returns the royalty information that all ids in this contract will default to. + +The returned tuple contains: + +- `t.0`: The receiver of the royalty payment. +- `t.1`: The numerator of the royalty fraction. +- `t.2`: The denominator of the royalty fraction. + +#### [](#ERC2981InfoImpl-token_royalty)`token_royalty(self: @ContractState, token_id: u256) → (ContractAddress, u128, u128)` external + +Returns the royalty information specific to a token. If no specific royalty information is set for the token, the default is returned. + +The returned tuple contains: + +- `t.0`: The receiver of the royalty payment. +- `t.1`: The numerator of the royalty fraction. +- `t.2`: The denominator of the royalty fraction. + +#### [](#ERC2981Component-ERC2981AdminOwnableImpl)ERC2981AdminOwnableImpl + +Provides admin functions for managing royalty settings that are restricted to be called only by the contract’s owner. Requires the contract to implement [OwnableComponent](access#OwnableComponent). + +#### [](#ERC2981AdminOwnableImpl-set_default_royalty)`set_default_royalty(ref self: ContractState, receiver: ContractAddress, fee_numerator: u128)` external + +Sets the royalty information that all ids in this contract will default to. + +Requirements: + +- The caller is the contract owner. +- `receiver` cannot be the zero address. +- `fee_numerator` cannot be greater than the fee denominator. + +#### [](#ERC2981AdminOwnableImpl-delete_default_royalty)`delete_default_royalty(ref self: ContractState)` external + +Sets the default royalty percentage and receiver to zero. + +Requirements: + +- The caller is the contract owner. + +#### [](#ERC2981AdminOwnableImpl-set_token_royalty)`set_token_royalty(ref self: ContractState, token_id: u256, receiver: ContractAddress, fee_numerator: u128)` external + +Sets the royalty information for a specific token id that takes precedence over the global default. + +Requirements: + +- The caller is the contract owner. +- `receiver` cannot be the zero address. +- `fee_numerator` cannot be greater than the fee denominator. + +#### [](#ERC2981AdminOwnableImpl-reset_token_royalty)`reset_token_royalty(ref self: ContractState, token_id: u256)` external + +Resets royalty information for the token id back to unset. + +Requirements: + +- The caller is the contract owner. + +#### [](#ERC2981Component-ERC2981AdminAccessControlImpl)ERC2981AdminAccessControlImpl + +Provides admin functions for managing royalty settings that require `ROYALTY_ADMIN_ROLE` to be granted to the caller. Requires the contract to implement [AccessControlComponent](access#AccessControlComponent). + +#### [](#ERC2981AdminAccessControlImpl-ROYALTY_ADMIN_ROLE)`ROYALTY_ADMIN_ROLE: felt252` constant + +Role for the admin responsible for managing royalty settings. + +#### [](#ERC2981AdminAccessControlImpl-set_default_royalty)`set_default_royalty(ref self: ContractState, receiver: ContractAddress, fee_numerator: u128)` external + +Sets the royalty information that all ids in this contract will default to. + +Requirements: + +- The caller must have `ROYALTY_ADMIN_ROLE` role. +- `receiver` cannot be the zero address. +- `fee_numerator` cannot be greater than the fee denominator. + +#### [](#ERC2981AdminAccessControlImpl-delete_default_royalty)`delete_default_royalty(ref self: ContractState)` external + +Sets the default royalty percentage and receiver to zero. + +Requirements: + +- The caller must have `ROYALTY_ADMIN_ROLE` role. + +#### [](#ERC2981AdminAccessControlImpl-set_token_royalty)`set_token_royalty(ref self: ContractState, token_id: u256, receiver: ContractAddress, fee_numerator: u128)` external + +Sets the royalty information for a specific token id that takes precedence over the global default. + +Requirements: + +- The caller must have `ROYALTY_ADMIN_ROLE` role. +- `receiver` cannot be the zero address. +- `fee_numerator` cannot be greater than the fee denominator. + +#### [](#ERC2981AdminAccessControlImpl-reset_token_royalty)`reset_token_royalty(ref self: ContractState, token_id: u256)` external + +Resets royalty information for the token id back to unset. + +Requirements: + +- The caller must have `ROYALTY_ADMIN_ROLE` role. + +#### [](#ERC2981Component-Internal-functions)Internal functions + +#### [](#ERC2981Component-initializer)`initializer(ref self: ContractState, default_receiver: ContractAddress, default_royalty_fraction: u128)` internal + +Initializes the contract by setting the default royalty and registering the supported interface. + +Requirements: + +- `default_receiver` cannot be the zero address. +- `default_royalty_fraction` cannot be greater than the fee denominator. +- The fee denominator must be greater than 0. + +The fee denominator is set by the contract using the [Immutable Component Config](../components#immutable_config). + +#### [](#ERC2981Component-_default_royalty)`_default_royalty(self: @ContractState) → (ContractAddress, u128, u128)` internal + +Returns the royalty information that all ids in this contract will default to. + +The returned tuple contains: + +- `t.0`: The receiver of the royalty payment. +- `t.1`: The numerator of the royalty fraction. +- `t.2`: The denominator of the royalty fraction. + +#### [](#ERC2981Component-_set_default_royalty)`_set_default_royalty(ref self: ContractState, receiver: ContractAddress, fee_numerator: u128)` internal + +Sets the royalty information that all ids in this contract will default to. + +Requirements: + +- `receiver` cannot be the zero address. +- `fee_numerator` cannot be greater than the fee denominator. + +#### [](#ERC2981Component-_delete_default_royalty)`_delete_default_royalty(ref self: ContractState)` internal + +Sets the default royalty percentage and receiver to zero. + +#### [](#ERC2981Component-_token_royalty)`_token_royalty(self: @ContractState, token_id: u256) → (ContractAddress, u256, u256)` internal + +Returns the royalty information that all ids in this contract will default to. + +The returned tuple contains: + +- `t.0`: The receiver of the royalty payment. +- `t.1`: The numerator of the royalty fraction. +- `t.2`: The denominator of the royalty fraction. + +#### [](#ERC2981Component-_set_token_royalty)`_set_token_royalty(ref self: ContractState, token_id: u256, receiver: ContractAddress, fee_numerator: u128)` internal + +Sets the royalty information for a specific token id that takes precedence over the global default. + +Requirements: + +- `receiver` cannot be the zero address. +- `fee_numerator` cannot be greater than the fee denominator. + +#### [](#ERC2981Component-_reset_token_royalty)`_reset_token_royalty(ref self: ContractState, token_id: u256)` internal + +Resets royalty information for the token id back to unset. diff --git a/content/cairo-contracts/api/udc.mdx b/content/cairo-contracts/api/udc.mdx new file mode 100644 index 00000000..73464836 --- /dev/null +++ b/content/cairo-contracts/api/udc.mdx @@ -0,0 +1,65 @@ +--- +title: Universal Deployer +--- + +Reference of the Universal Deployer Contract (UDC) interface and preset. + +## [](#interfaces)Interfaces + +Starting from version `3.x.x`, the interfaces are no longer part of the `openzeppelin_utils` package. The references documented here are contained in the `openzeppelin_interfaces` package version `v2.1.0`. + +### [](#IUniversalDeployer)`IUniversalDeployer`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/interfaces/src/utils/deployments.cairo) + +```cairo +use openzeppelin_interfaces::deployments::IUniversalDeployer; +``` + +Functions + +- [`deploy_contract(class_hash, salt, not_from_zero, calldata)`](#IUniversalDeployer-deploy_contract) + +Events + +- [`ContractDeployed(address, deployer, not_from_zero, class_hash, calldata, salt)`](#IUniversalDeployer-ContractDeployed) + +#### [](#IUniversalDeployer-Functions)Functions + +#### [](#IUniversalDeployer-deploy_contract)`deploy_contract(class_hash: ClassHash, salt: felt252, not_from_zero: bool, calldata: Span) → ContractAddress` external + +Deploys a contract through the Universal Deployer Contract. + +#### [](#IUniversalDeployer-Events)Events + +#### [](#IUniversalDeployer-ContractDeployed)`ContractDeployed(address: ContractAddress, deployer: ContractAddress, not_from_zero: bool, class_hash: ClassHash, calldata: Span, salt: felt252)` event + +Emitted when `deployer` deploys a contract through the Universal Deployer Contract. + +## [](#presets)Presets + +### [](#UniversalDeployer)`UniversalDeployer`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/presets/src/universal_deployer.cairo) + +```cairo +use openzeppelin_presets::UniversalDeployer; +``` + +The standard Universal Deployer Contract. + +[Sierra class hash](../presets) + +0x037f5901deb2b20bf5b2ddb04f6c770a7c5581edd68aa49f199cf74dfaf03c06 + +Embedded Implementations + +UniversalDeployerImpl + +- [`deploy_contract(self, address, deployer, not_from_zero, class_hash, calldata, salt)`](#UniversalDeployer-deploy_contract) + +#### [](#UniversalDeployer-deploy_contract)`deploy_contract(ref self: ContractState, address: ContractAddress, deployer: ContractAddress, not_from_zero: bool, class_hash: ClassHash, calldata: Span, salt: felt252) -> ContractAddress` external + +Deploys a contract through the Universal Deployer Contract. + +When `not_from_zero` is `true`, `salt` is hashed with the caller address and the modified salt is passed to the inner `deploy_syscall`. This type of deployment is [origin-dependent](../udc#origin_dependent). + +When `not_from_zero` is `false`, the deployment type is [origin-independent](../udc#origin_independent). + +Emits an [ContractDeployed](#IUniversalDeployer-ContractDeployed) event. diff --git a/content/cairo-contracts/api/upgrades.mdx b/content/cairo-contracts/api/upgrades.mdx new file mode 100644 index 00000000..8b911fd2 --- /dev/null +++ b/content/cairo-contracts/api/upgrades.mdx @@ -0,0 +1,102 @@ +--- +title: Upgrades +--- + +This crate provides interfaces and utilities related to upgradeability. + +## [](#interfaces)Interfaces + +Starting from version `3.x.x`, the interfaces are no longer part of the `openzeppelin_upgrades` package. The references documented here are contained in the `openzeppelin_interfaces` package version `v2.1.0`. + +### [](#IUpgradeable)`IUpgradeable`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/interfaces/src/upgrades.cairo) + +```cairo +use openzeppelin_interfaces::upgrades::IUpgradeable; +``` + +Interface of an upgradeable contract. + +Functions + +- [`upgrade(new_class_hash)`](#IUpgradeable-upgrade) + +#### [](#IUpgradeable-Functions)Functions + +#### [](#IUpgradeable-upgrade)`upgrade(new_class_hash: ClassHash)` external + +Upgrades the contract code by updating its [class hash](https://docs.starknet.io/architecture-and-concepts/smart-contracts/class-hash/). + +This function is usually protected by an [Access Control](../access) mechanism. + +### [](#IUpgradeAndCall)`IUpgradeAndCall`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/interfaces/src/upgrades.cairo) + +```cairo +use openzeppelin_interfaces::upgrades::IUpgradeAndCall; +``` + +Interface for an upgradeable contract that couples an upgrade with a function call in the upgraded context. + +Functions + +- [`upgrade_and_call(new_class_hash, selector, calldata)`](#IUpgradeAndCall-upgrade_and_call) + +#### [](#IUpgradeAndCall-Functions)Functions + +#### [](#IUpgradeAndCall-upgrade_and_call)`upgrade_and_call(new_class_hash: ClassHash, selector: felt252, calldata: Span) → Span` external + +Upgrades the contract code by updating its [class hash](https://docs.starknet.io/architecture-and-concepts/smart-contracts/class-hash/) and calls `selector` with the upgraded context. + +This function is usually protected by an [Access Control](../access) mechanism. + +## [](#core)Core + +### [](#UpgradeableComponent)`UpgradeableComponent`[](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v3.0.0-alpha.1/packages/upgrades/src/upgradeable.cairo) + +```cairo +use openzeppelin_upgrades::upgradeable::UpgradeableComponent; +``` + +Upgradeable component. + +Internal Implementations + +InternalImpl + +- [`upgrade(self, new_class_hash)`](#UpgradeableComponent-upgrade) +- [`upgrade_and_call(self, new_class_hash, selector, calldata)`](#UpgradeableComponent-upgrade_and_call) + +Events + +- [`Upgraded(class_hash)`](#UpgradeableComponent-Upgraded) + +#### [](#UpgradeableComponent-Internal-Functions)Internal Functions + +#### [](#UpgradeableComponent-upgrade)`upgrade(ref self: ContractState, new_class_hash: ClassHash)` internal + +Upgrades the contract by updating the contract [class hash](https://docs.starknet.io/architecture-and-concepts/smart-contracts/class-hash/). + +Requirements: + +- `new_class_hash` must be different from zero. + +Emits an [Upgraded](#UpgradeableComponent-Upgraded) event. + +#### [](#UpgradeableComponent-upgrade_and_call)`upgrade_and_call(ref self: ContractState, new_class_hash: ClassHash, selector: felt252, calldata: Span) → Span` internal + +Replaces the contract’s class hash with `new_class_hash` and then calls `selector` from the upgraded context. This function returns the unwrapped `call_contract_syscall` return value(s), if available, of the `selector` call. + +Requirements: + +- `new_class_hash` must be different from zero. + +The function call comes from the upgraded contract itself and not the account. + +A similar behavior to `upgrade_and_call` can also be achieved with a list of calls from an account since the [SNIP-6](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-6.md) account standard supports multicall. An account can execute a list of calls with [upgrade](#IUpgradeable-upgrade) being the first element in the list and the extra function call as the second. With this approach, the calls will execute from the account’s context and can’t be front-ran. + +Emits an [Upgraded](#UpgradeableComponent-Upgraded) event. + +#### [](#UpgradeableComponent-Events)Events + +#### [](#UpgradeableComponent-Upgraded)`Upgraded(class_hash: ClassHash)` event + +Emitted when the [class hash](https://docs.starknet.io/architecture-and-concepts/smart-contracts/class-hash/) is upgraded. diff --git a/content/cairo-contracts/api/utilities.mdx b/content/cairo-contracts/api/utilities.mdx new file mode 100644 index 00000000..ea802705 --- /dev/null +++ b/content/cairo-contracts/api/utilities.mdx @@ -0,0 +1,283 @@ +--- +title: Utilities +--- + +This crate provides miscellaneous components and libraries containing utility functions to handle common tasks. + +## [](#core)Core + +### [](#utils)`utils` + +```cairo +use openzeppelin_utils; +``` + +Module containing core utilities of the library. + +Members + +Inner modules + +- [`cryptography`](#utils-cryptography) +- [`deployments`](#utils-deployments) +- [`math`](#utils-math) +- [`contract_clock`](#utils-contract_clock) +- [`serde`](#utils-serde) + +#### [](#utils-Inner-Modules)Inner modules + +#### [](#utils-cryptography)`cryptography` module + +See [`openzeppelin_utils::cryptography`](#cryptography). + +#### [](#utils-deployments)`deployments` module + +See [`openzeppelin_utils::deployments`](#deployments). + +#### [](#utils-math)`math` module + +See [`openzeppelin_utils::math`](#math). + +#### [](#utils-contract_clock)`contract_clock` module + +See [`openzeppelin_utils::contract_clock`](#contract_clock). + +#### [](#utils-serde)`serde` module + +See [`openzeppelin_utils::serde`](#serde). + +### [](#cryptography)`cryptography` + +```cairo +use openzeppelin_utils::cryptography; +``` + +Module containing utilities related to cryptography. + +Members + +Inner modules + +- [`nonces`](#cryptography-nonces) +- [`snip12`](#cryptography-snip12) + +#### [](#inner_modules)Inner modules + +#### [](#cryptography-nonces)`nonces` module + +See [`openzeppelin_utils::cryptography::nonces::NoncesComponent`](#NoncesComponent). + +#### [](#cryptography-snip12)`snip12` module + +See [`openzeppelin_utils::cryptography::snip12`](#snip12). + +### [](#deployments)`deployments` + +```cairo +use openzeppelin_utils::deployments; +``` + +Module containing utility functions for calculating contract addresses through [deploy\_syscall](https://docs.starknet.io/architecture-and-concepts/smart-contracts/system-calls-cairo1/#deploy) and the [Universal Deployer Contract](../udc) (UDC). + +Members + +Structs + +- [`DeployerInfo(caller_address, udc_address)`](#deployments-DeployerInfo) + +Functions + +- [`calculate_contract_address_from_deploy_syscall(salt, class_hash, constructor_calldata, deployer_address)`](#deployments-calculate_contract_address_from_deploy_syscall) +- [`compute_hash_on_elements(data)`](#deployments-compute_hash_on_elements) +- [`calculate_contract_address_from_udc(salt, class_hash, constructor_calldata, deployer_info)`](#deployments-calculate_contract_address_from_udc) + +#### [](#deployments-Structs)Structs + +#### [](#deployments-DeployerInfo)`DeployerInfo(caller_address: ContractAddress, udc_address: ContractAddress)` struct + +Struct containing arguments necessary in [utils::calculate\_contract\_address\_from\_udc](#deployments-calculate_contract_address_from_udc) for origin-dependent deployment calculations. + +#### [](#deployments-Functions)Functions + +#### [](#deployments-calculate_contract_address_from_deploy_syscall)`calculate_contract_address_from_deploy_syscall(salt: felt252, class_hash: ClassHash, constructor_calldata: Span, deployer_address: ContractAddress) → ContractAddress` function + +Returns the contract address when passing the given arguments to [deploy\_syscall](https://docs.starknet.io/architecture-and-concepts/smart-contracts/system-calls-cairo1/#deploy). + +#### [](#deployments-compute_hash_on_elements)`compute_hash_on_elements(data: Span) → felt252` function + +Creates a Pedersen hash chain with the elements of `data` and returns the finalized hash. + +#### [](#deployments-calculate_contract_address_from_udc)`calculate_contract_address_from_udc(salt: felt252, class_hash: ClassHash, constructor_calldata: Span, deployer_info: Option) → ContractAddress` function + +Returns the calculated contract address for UDC deployments. + +Origin-independent deployments (deployed from zero) should pass `Option::None` as `deployer_info`. + +Origin-dependent deployments hash `salt` with `caller_address` (member of [DeployerInfo](#deployments-DeployerInfo)) and pass the hashed salt to the inner [deploy\_syscall](https://docs.starknet.io/architecture-and-concepts/smart-contracts/system-calls-cairo1/#deploy) as the `contract_address_salt` argument. + +### [](#math)`math` + +```cairo +use openzeppelin_utils::math; +``` + +Module containing math utilities. + +Members + +Functions + +- [`average(a, b)`](#math-average) + +#### [](#math-Functions)Functions + +#### [](#math-average)`average(a: T, b: T) → T` function + +Returns the average of two unsigned integers. The result is rounded down. + +`T` is a generic value matching different numeric implementations. + +### [](#contract_clock)`contract_clock` + +```cairo +use openzeppelin_utils::contract_clock; +``` + +Module providing a trait for the [EIP-6372](https://eips.ethereum.org/EIPS/eip-6372) standard along with default clock implementations based on either block number or block timestamp. + +Traits + +- [`ERC6372Clock`](#ERC6372Clock) + +Implementations + +- [`ERC6372BlockNumberClock`](#contract_clock-ERC6372BlockNumberClock) +- [`ERC6372TimestampClock`](#contract_clock-ERC6372TimestampClock) + +#### [](#ERC6372Clock)`ERC6372Clock` [github-icon](https://github.com/OpenZeppelin/cairo-contracts/blob/votes-erc6372/packages/utils/src/contract_clock.cairo) + +```cairo +use openzeppelin_utils::contract_clock::ERC6372Clock; +``` + +A trait for the [EIP-6372](https://eips.ethereum.org/EIPS/eip-6372) standard that allows flexible internal clock implementation — based on block timestamp, block number, or a custom logic. + +Functions + +- [`clock()`](#ERC6372Clock-clock) +- [`CLOCK_MODE()`](#ERC6372Clock-CLOCK_MODE) + +#### [](#ERC6372Clock-Functions)Functions + +#### [](#ERC6372Clock-clock)`clock() → u64` external + +Returns the current timepoint determined by the contract’s operational mode, intended for use in time-sensitive logic. + +Requirements: + +- This function MUST always be non-decreasing. + +#### [](#ERC6372Clock-CLOCK_MODE)`CLOCK_MODE() → ByteArray` external + +Returns a description of the clock the contract is operating in. + +Requirements: + +- The output MUST be formatted like a URL query string, decodable in standard JavaScript. + +#### [](#contract_clock-Impls)Implementations + +#### [](#contract_clock-ERC6372BlockNumberClock)`ERC6372BlockNumberClock` impl + +Implementation of the `ERC6372Clock` trait that uses the block number as its clock reference. + +#### [](#contract_clock-ERC6372TimestampClock)`ERC6372TimestampClock` impl + +Implementation of the `ERC6372Clock` trait that uses the block timestamp as its clock reference. + +### [](#serde)`serde` + +```cairo +use openzeppelin_utils::serde; +``` + +Module containing utilities related to serialization and deserialization of Cairo data structures. + +Members + +Traits + +- [`SerializedAppend`](#serde-SerializedAppend) + +#### [](#serde-Traits)Traits + +#### [](#serde-SerializedAppend)`SerializedAppend` trait + +Importing this trait allows the ability to append a serialized representation of a Cairo data structure already implementing the `Serde` trait to a `felt252` buffer. + +Usage example: + +```cairo +use openzeppelin_utils::serde::SerializedAppend; +use starknet::ContractAddress; + +fn to_calldata(recipient: ContractAddress, amount: u256) -> Array { + let mut calldata = array![]; + calldata.append_serde(recipient); + calldata.append_serde(amount); + calldata +} +``` + +Note that the `append_serde` method is automatically available for arrays of felts, and it accepts any data structure that implements the `Serde` trait. + +## [](#cryptography_2)Cryptography + +### [](#NoncesComponent)`NoncesComponent` + +```cairo +use openzeppelin_utils::cryptography::nonces::NoncesComponent; +``` + +This component provides a simple mechanism for handling incremental nonces for a set of addresses. It is commonly used to prevent replay attacks when contracts accept signatures as input. + +Embeddable Implementations + +NoncesImpl + +- [`nonces(self, owner)`](#NoncesComponent-nonces) + +Internal Implementations + +InternalImpl + +- [`use_nonce(self, owner)`](#NoncesComponent-use_nonce) +- [`use_checked_nonce(self, owner, nonce)`](#NoncesComponent-use_checked_nonce) + +#### [](#NoncesComponent-Embeddable-Functions)Embeddable functions + +#### [](#NoncesComponent-nonces)`nonces(self: @ContractState, owner: ContractAddress) → felt252` external + +Returns the next unused nonce for an `owner`. + +#### [](#NoncesComponent-Internal-Functions)Internal functions + +#### [](#NoncesComponent-use_nonce)`use_nonce(ref self: ComponentState, owner: ContractAddress) → felt252` internal + +Consumes a nonce, returns the current value, and increments nonce. + +For each account, the nonce has an initial value of 0, can only be incremented by one, and cannot be decremented or reset. This guarantees that the nonce never overflows. + +#### [](#NoncesComponent-use_checked_nonce)`use_checked_nonce(ref self: ComponentState, owner: ContractAddress, nonce: felt252) → felt252` internal + +Same as `use_nonce` but checking that `nonce` is the next valid one for `owner`. + +### [](#snip12)`snip12` + +```cairo +use openzeppelin_utils::snip12; +``` + +Supports on-chain generation of message hashes compliant with [SNIP12](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-12.md). + +For a full walkthrough on how to use this module, see the [SNIP12 and Typed Messages](../guides/snip12) guide. diff --git a/src/navigation/starknet.json b/src/navigation/starknet.json index cbeb50f3..9ea0dfdb 100644 --- a/src/navigation/starknet.json +++ b/src/navigation/starknet.json @@ -177,6 +177,82 @@ } ] }, + { + "type": "folder", + "name": "API Reference", + "children": [ + { + "type": "page", + "name": "Access Control", + "url": "/cairo-contracts/api/access" + }, + { + "type": "page", + "name": "Account", + "url": "/cairo-contracts/api/account" + }, + { + "type": "page", + "name": "ERC-1155", + "url": "/cairo-contracts/api/erc1155" + }, + { + "type": "page", + "name": "ERC-20", + "url": "/cairo-contracts/api/erc20" + }, + { + "type": "page", + "name": "ERC-721", + "url": "/cairo-contracts/api/erc721" + }, + { + "type": "page", + "name": "Finance", + "url": "/cairo-contracts/api/finance" + }, + { + "type": "page", + "name": "Governance", + "url": "/cairo-contracts/api/governance" + }, + { + "type": "page", + "name": "Introspection", + "url": "/cairo-contracts/api/introspection" + }, + { + "type": "page", + "name": "Merkle Tree", + "url": "/cairo-contracts/api/merkle-tree" + }, + { + "type": "page", + "name": "Security", + "url": "/cairo-contracts/api/security" + }, + { + "type": "page", + "name": "Token Common", + "url": "/cairo-contracts/api/token_common" + }, + { + "type": "page", + "name": "UDC", + "url": "/cairo-contracts/api/udc" + }, + { + "type": "page", + "name": "Upgrades", + "url": "/cairo-contracts/api/upgrades" + }, + { + "type": "page", + "name": "Utilities", + "url": "/cairo-contracts/api/utilities" + } + ] + }, { "type": "folder", "name": "Utils", From b5a3fadb39bef8242f18a1cb81b7581aef6deb42 Mon Sep 17 00:00:00 2001 From: Steve Date: Mon, 8 Sep 2025 16:51:45 -0400 Subject: [PATCH 05/14] chore: added replace-brackets back --- scripts/replace-brackets.js | 40 +++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 scripts/replace-brackets.js diff --git a/scripts/replace-brackets.js b/scripts/replace-brackets.js new file mode 100644 index 00000000..52b30249 --- /dev/null +++ b/scripts/replace-brackets.js @@ -0,0 +1,40 @@ +const fs = require("fs"); +const path = require("path"); + +function processFile(filePath) { + try { + const content = fs.readFileSync(filePath, "utf8"); + // Preserve brackets inside code fences (```...```) + const modifiedContent = content.replace(/```[\s\S]*?```|[{}]/g, (match) => { + // If match contains newlines or starts with ```, it's a code block - preserve it + return match.includes("\n") || match.startsWith("```") ? match : ""; + }); + fs.writeFileSync(filePath, modifiedContent, "utf8"); + console.log(`Processed: ${filePath}`); + } catch (error) { + console.error(`Error processing ${filePath}: ${error.message}`); + } +} + +function crawlDirectory(dirPath) { + try { + const items = fs.readdirSync(dirPath); + + for (const item of items) { + const itemPath = path.join(dirPath, item); + const stats = fs.statSync(itemPath); + + if (stats.isDirectory()) { + crawlDirectory(itemPath); + } else if (stats.isFile()) { + processFile(itemPath); + } + } + } catch (error) { + console.error(`Error crawling directory ${dirPath}: ${error.message}`); + } +} + +// Start crawling from current directory or specify a path +const targetPath = process.argv[2] || "."; +crawlDirectory(targetPath); From fd55b8f0f2f53f578aa3752146a818d6180b18ec Mon Sep 17 00:00:00 2001 From: Steve Date: Mon, 8 Sep 2025 17:04:42 -0400 Subject: [PATCH 06/14] feat: added openapi components --- .../openzeppelin-relayer/api/callPlugin.mdx | 15 + .../api/cancelTransaction.mdx | 15 + .../api/createNotification.mdx | 15 + .../api/createRelayer.mdx | 15 + .../openzeppelin-relayer/api/createSigner.mdx | 15 + .../api/deleteNotification.mdx | 15 + .../api/deletePendingTransactions.mdx | 15 + .../api/deleteRelayer.mdx | 15 + .../openzeppelin-relayer/api/deleteSigner.mdx | 15 + .../api/getNotification.mdx | 15 + .../openzeppelin-relayer/api/getRelayer.mdx | 15 + .../api/getRelayerBalance.mdx | 15 + .../api/getRelayerStatus.mdx | 15 + .../openzeppelin-relayer/api/getSigner.mdx | 15 + .../api/getTransactionById.mdx | 15 + .../api/getTransactionByNonce.mdx | 15 + content/openzeppelin-relayer/api/health.mdx | 29 ++ .../api/listNotifications.mdx | 25 + .../openzeppelin-relayer/api/listRelayers.mdx | 25 + .../openzeppelin-relayer/api/listSigners.mdx | 25 + .../api/listTransactions.mdx | 15 + .../openzeppelin-relayer/api/list_metrics.mdx | 33 ++ .../api/metric_detail.mdx | 38 ++ .../api/replaceTransaction.mdx | 15 + content/openzeppelin-relayer/api/rpc.mdx | 15 + .../api/scrape_metrics.mdx | 30 ++ .../api/sendTransaction.mdx | 15 + content/openzeppelin-relayer/api/sign.mdx | 15 + .../api/signTransaction.mdx | 15 + .../api/signTypedData.mdx | 15 + .../api/updateNotification.mdx | 15 + .../api/updateRelayer.mdx | 15 + .../openzeppelin-relayer/api/updateSigner.mdx | 15 + package.json | 1 + pnpm-lock.yaml | 433 ++++++++++++++++++ scripts/generate-openapi-docs.js | 10 + src/app/global.css | 1 + src/lib/openapi.ts | 8 + src/lib/source.ts | 4 + src/mdx-components.tsx | 3 + src/navigation/ethereum-evm.json | 173 ++++++- src/navigation/tools.json | 171 ++++++- 42 files changed, 1392 insertions(+), 7 deletions(-) create mode 100644 content/openzeppelin-relayer/api/callPlugin.mdx create mode 100644 content/openzeppelin-relayer/api/cancelTransaction.mdx create mode 100644 content/openzeppelin-relayer/api/createNotification.mdx create mode 100644 content/openzeppelin-relayer/api/createRelayer.mdx create mode 100644 content/openzeppelin-relayer/api/createSigner.mdx create mode 100644 content/openzeppelin-relayer/api/deleteNotification.mdx create mode 100644 content/openzeppelin-relayer/api/deletePendingTransactions.mdx create mode 100644 content/openzeppelin-relayer/api/deleteRelayer.mdx create mode 100644 content/openzeppelin-relayer/api/deleteSigner.mdx create mode 100644 content/openzeppelin-relayer/api/getNotification.mdx create mode 100644 content/openzeppelin-relayer/api/getRelayer.mdx create mode 100644 content/openzeppelin-relayer/api/getRelayerBalance.mdx create mode 100644 content/openzeppelin-relayer/api/getRelayerStatus.mdx create mode 100644 content/openzeppelin-relayer/api/getSigner.mdx create mode 100644 content/openzeppelin-relayer/api/getTransactionById.mdx create mode 100644 content/openzeppelin-relayer/api/getTransactionByNonce.mdx create mode 100644 content/openzeppelin-relayer/api/health.mdx create mode 100644 content/openzeppelin-relayer/api/listNotifications.mdx create mode 100644 content/openzeppelin-relayer/api/listRelayers.mdx create mode 100644 content/openzeppelin-relayer/api/listSigners.mdx create mode 100644 content/openzeppelin-relayer/api/listTransactions.mdx create mode 100644 content/openzeppelin-relayer/api/list_metrics.mdx create mode 100644 content/openzeppelin-relayer/api/metric_detail.mdx create mode 100644 content/openzeppelin-relayer/api/replaceTransaction.mdx create mode 100644 content/openzeppelin-relayer/api/rpc.mdx create mode 100644 content/openzeppelin-relayer/api/scrape_metrics.mdx create mode 100644 content/openzeppelin-relayer/api/sendTransaction.mdx create mode 100644 content/openzeppelin-relayer/api/sign.mdx create mode 100644 content/openzeppelin-relayer/api/signTransaction.mdx create mode 100644 content/openzeppelin-relayer/api/signTypedData.mdx create mode 100644 content/openzeppelin-relayer/api/updateNotification.mdx create mode 100644 content/openzeppelin-relayer/api/updateRelayer.mdx create mode 100644 content/openzeppelin-relayer/api/updateSigner.mdx create mode 100644 scripts/generate-openapi-docs.js create mode 100644 src/lib/openapi.ts diff --git a/content/openzeppelin-relayer/api/callPlugin.mdx b/content/openzeppelin-relayer/api/callPlugin.mdx new file mode 100644 index 00000000..fe557873 --- /dev/null +++ b/content/openzeppelin-relayer/api/callPlugin.mdx @@ -0,0 +1,15 @@ +--- +title: Calls a plugin method. +full: true +_openapi: + method: POST + route: /api/v1/plugins/{plugin_id}/call + toc: [] + structuredData: + headings: [] + contents: [] +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/content/openzeppelin-relayer/api/cancelTransaction.mdx b/content/openzeppelin-relayer/api/cancelTransaction.mdx new file mode 100644 index 00000000..5e048adf --- /dev/null +++ b/content/openzeppelin-relayer/api/cancelTransaction.mdx @@ -0,0 +1,15 @@ +--- +title: Cancels a specific transaction by its ID. +full: true +_openapi: + method: DELETE + route: /api/v1/relayers/{relayer_id}/transactions/{transaction_id} + toc: [] + structuredData: + headings: [] + contents: [] +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/content/openzeppelin-relayer/api/createNotification.mdx b/content/openzeppelin-relayer/api/createNotification.mdx new file mode 100644 index 00000000..9b979508 --- /dev/null +++ b/content/openzeppelin-relayer/api/createNotification.mdx @@ -0,0 +1,15 @@ +--- +title: Creates a new notification. +full: true +_openapi: + method: POST + route: /api/v1/notifications + toc: [] + structuredData: + headings: [] + contents: [] +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/content/openzeppelin-relayer/api/createRelayer.mdx b/content/openzeppelin-relayer/api/createRelayer.mdx new file mode 100644 index 00000000..a6f15a08 --- /dev/null +++ b/content/openzeppelin-relayer/api/createRelayer.mdx @@ -0,0 +1,15 @@ +--- +title: Creates a new relayer. +full: true +_openapi: + method: POST + route: /api/v1/relayers + toc: [] + structuredData: + headings: [] + contents: [] +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/content/openzeppelin-relayer/api/createSigner.mdx b/content/openzeppelin-relayer/api/createSigner.mdx new file mode 100644 index 00000000..8ef67359 --- /dev/null +++ b/content/openzeppelin-relayer/api/createSigner.mdx @@ -0,0 +1,15 @@ +--- +title: Creates a new signer. +full: true +_openapi: + method: POST + route: /api/v1/signers + toc: [] + structuredData: + headings: [] + contents: [] +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/content/openzeppelin-relayer/api/deleteNotification.mdx b/content/openzeppelin-relayer/api/deleteNotification.mdx new file mode 100644 index 00000000..e79a94a6 --- /dev/null +++ b/content/openzeppelin-relayer/api/deleteNotification.mdx @@ -0,0 +1,15 @@ +--- +title: Deletes a notification by ID. +full: true +_openapi: + method: DELETE + route: /api/v1/notifications/{notification_id} + toc: [] + structuredData: + headings: [] + contents: [] +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/content/openzeppelin-relayer/api/deletePendingTransactions.mdx b/content/openzeppelin-relayer/api/deletePendingTransactions.mdx new file mode 100644 index 00000000..629f7246 --- /dev/null +++ b/content/openzeppelin-relayer/api/deletePendingTransactions.mdx @@ -0,0 +1,15 @@ +--- +title: Deletes all pending transactions for a specific relayer. +full: true +_openapi: + method: DELETE + route: /api/v1/relayers/{relayer_id}/transactions/pending + toc: [] + structuredData: + headings: [] + contents: [] +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/content/openzeppelin-relayer/api/deleteRelayer.mdx b/content/openzeppelin-relayer/api/deleteRelayer.mdx new file mode 100644 index 00000000..108df063 --- /dev/null +++ b/content/openzeppelin-relayer/api/deleteRelayer.mdx @@ -0,0 +1,15 @@ +--- +title: Deletes a relayer by ID. +full: true +_openapi: + method: DELETE + route: /api/v1/relayers/{relayer_id} + toc: [] + structuredData: + headings: [] + contents: [] +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/content/openzeppelin-relayer/api/deleteSigner.mdx b/content/openzeppelin-relayer/api/deleteSigner.mdx new file mode 100644 index 00000000..1eac45c0 --- /dev/null +++ b/content/openzeppelin-relayer/api/deleteSigner.mdx @@ -0,0 +1,15 @@ +--- +title: Deletes a signer by ID. +full: true +_openapi: + method: DELETE + route: /api/v1/signers/{signer_id} + toc: [] + structuredData: + headings: [] + contents: [] +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/content/openzeppelin-relayer/api/getNotification.mdx b/content/openzeppelin-relayer/api/getNotification.mdx new file mode 100644 index 00000000..fb83d496 --- /dev/null +++ b/content/openzeppelin-relayer/api/getNotification.mdx @@ -0,0 +1,15 @@ +--- +title: Retrieves details of a specific notification by ID. +full: true +_openapi: + method: GET + route: /api/v1/notifications/{notification_id} + toc: [] + structuredData: + headings: [] + contents: [] +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/content/openzeppelin-relayer/api/getRelayer.mdx b/content/openzeppelin-relayer/api/getRelayer.mdx new file mode 100644 index 00000000..a587175d --- /dev/null +++ b/content/openzeppelin-relayer/api/getRelayer.mdx @@ -0,0 +1,15 @@ +--- +title: Retrieves details of a specific relayer by ID. +full: true +_openapi: + method: GET + route: /api/v1/relayers/{relayer_id} + toc: [] + structuredData: + headings: [] + contents: [] +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/content/openzeppelin-relayer/api/getRelayerBalance.mdx b/content/openzeppelin-relayer/api/getRelayerBalance.mdx new file mode 100644 index 00000000..78808265 --- /dev/null +++ b/content/openzeppelin-relayer/api/getRelayerBalance.mdx @@ -0,0 +1,15 @@ +--- +title: Retrieves the balance of a specific relayer. +full: true +_openapi: + method: GET + route: /api/v1/relayers/{relayer_id}/balance + toc: [] + structuredData: + headings: [] + contents: [] +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/content/openzeppelin-relayer/api/getRelayerStatus.mdx b/content/openzeppelin-relayer/api/getRelayerStatus.mdx new file mode 100644 index 00000000..158e831f --- /dev/null +++ b/content/openzeppelin-relayer/api/getRelayerStatus.mdx @@ -0,0 +1,15 @@ +--- +title: Fetches the current status of a specific relayer. +full: true +_openapi: + method: GET + route: /api/v1/relayers/{relayer_id}/status + toc: [] + structuredData: + headings: [] + contents: [] +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/content/openzeppelin-relayer/api/getSigner.mdx b/content/openzeppelin-relayer/api/getSigner.mdx new file mode 100644 index 00000000..cef67a33 --- /dev/null +++ b/content/openzeppelin-relayer/api/getSigner.mdx @@ -0,0 +1,15 @@ +--- +title: Retrieves details of a specific signer by ID. +full: true +_openapi: + method: GET + route: /api/v1/signers/{signer_id} + toc: [] + structuredData: + headings: [] + contents: [] +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/content/openzeppelin-relayer/api/getTransactionById.mdx b/content/openzeppelin-relayer/api/getTransactionById.mdx new file mode 100644 index 00000000..f2672ef9 --- /dev/null +++ b/content/openzeppelin-relayer/api/getTransactionById.mdx @@ -0,0 +1,15 @@ +--- +title: Retrieves a specific transaction by its ID. +full: true +_openapi: + method: GET + route: /api/v1/relayers/{relayer_id}/transactions/{transaction_id} + toc: [] + structuredData: + headings: [] + contents: [] +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/content/openzeppelin-relayer/api/getTransactionByNonce.mdx b/content/openzeppelin-relayer/api/getTransactionByNonce.mdx new file mode 100644 index 00000000..c3565b0c --- /dev/null +++ b/content/openzeppelin-relayer/api/getTransactionByNonce.mdx @@ -0,0 +1,15 @@ +--- +title: Retrieves a transaction by its nonce value. +full: true +_openapi: + method: GET + route: /api/v1/relayers/{relayer_id}/transactions/by-nonce/{nonce} + toc: [] + structuredData: + headings: [] + contents: [] +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/content/openzeppelin-relayer/api/health.mdx b/content/openzeppelin-relayer/api/health.mdx new file mode 100644 index 00000000..75a5d483 --- /dev/null +++ b/content/openzeppelin-relayer/api/health.mdx @@ -0,0 +1,29 @@ +--- +title: Health routes implementation +full: true +_openapi: + method: GET + route: /v1/health + toc: [] + structuredData: + headings: [] + contents: + - content: >- + Note: OpenAPI documentation for these endpoints can be found in the + `openapi.rs` file + + Handles the `/health` endpoint. + + + Returns an `HttpResponse` with a status of `200 OK` and a body of + `"OK"`. +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Note: OpenAPI documentation for these endpoints can be found in the `openapi.rs` file +Handles the `/health` endpoint. + +Returns an `HttpResponse` with a status of `200 OK` and a body of `"OK"`. + + \ No newline at end of file diff --git a/content/openzeppelin-relayer/api/listNotifications.mdx b/content/openzeppelin-relayer/api/listNotifications.mdx new file mode 100644 index 00000000..a45ffb04 --- /dev/null +++ b/content/openzeppelin-relayer/api/listNotifications.mdx @@ -0,0 +1,25 @@ +--- +title: Notification routes implementation +full: true +_openapi: + method: GET + route: /api/v1/notifications + toc: [] + structuredData: + headings: [] + contents: + - content: >- + Note: OpenAPI documentation for these endpoints can be found in the + `openapi.rs` file + + + Lists all notifications with pagination support. +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Note: OpenAPI documentation for these endpoints can be found in the `openapi.rs` file + +Lists all notifications with pagination support. + + \ No newline at end of file diff --git a/content/openzeppelin-relayer/api/listRelayers.mdx b/content/openzeppelin-relayer/api/listRelayers.mdx new file mode 100644 index 00000000..77db3010 --- /dev/null +++ b/content/openzeppelin-relayer/api/listRelayers.mdx @@ -0,0 +1,25 @@ +--- +title: Relayer routes implementation +full: true +_openapi: + method: GET + route: /api/v1/relayers + toc: [] + structuredData: + headings: [] + contents: + - content: >- + Note: OpenAPI documentation for these endpoints can be found in the + `openapi.rs` file + + + Lists all relayers with pagination support. +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Note: OpenAPI documentation for these endpoints can be found in the `openapi.rs` file + +Lists all relayers with pagination support. + + \ No newline at end of file diff --git a/content/openzeppelin-relayer/api/listSigners.mdx b/content/openzeppelin-relayer/api/listSigners.mdx new file mode 100644 index 00000000..1cbd571d --- /dev/null +++ b/content/openzeppelin-relayer/api/listSigners.mdx @@ -0,0 +1,25 @@ +--- +title: Signer routes implementation +full: true +_openapi: + method: GET + route: /api/v1/signers + toc: [] + structuredData: + headings: [] + contents: + - content: >- + Note: OpenAPI documentation for these endpoints can be found in the + `openapi.rs` file + + + Lists all signers with pagination support. +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Note: OpenAPI documentation for these endpoints can be found in the `openapi.rs` file + +Lists all signers with pagination support. + + \ No newline at end of file diff --git a/content/openzeppelin-relayer/api/listTransactions.mdx b/content/openzeppelin-relayer/api/listTransactions.mdx new file mode 100644 index 00000000..5235f3aa --- /dev/null +++ b/content/openzeppelin-relayer/api/listTransactions.mdx @@ -0,0 +1,15 @@ +--- +title: Lists all transactions for a specific relayer with pagination. +full: true +_openapi: + method: GET + route: /api/v1/relayers/{relayer_id}/transactions/ + toc: [] + structuredData: + headings: [] + contents: [] +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/content/openzeppelin-relayer/api/list_metrics.mdx b/content/openzeppelin-relayer/api/list_metrics.mdx new file mode 100644 index 00000000..66a0f51f --- /dev/null +++ b/content/openzeppelin-relayer/api/list_metrics.mdx @@ -0,0 +1,33 @@ +--- +title: Metrics routes implementation +full: true +_openapi: + method: GET + route: /metrics + toc: [] + structuredData: + headings: [] + contents: + - content: >- + Note: OpenAPI documentation for these endpoints can be found in the + `openapi.rs` file + + Returns a list of all available metric names in JSON format. + + + # Returns + + + An `HttpResponse` containing a JSON array of metric names. +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Note: OpenAPI documentation for these endpoints can be found in the `openapi.rs` file +Returns a list of all available metric names in JSON format. + +# Returns + +An `HttpResponse` containing a JSON array of metric names. + + \ No newline at end of file diff --git a/content/openzeppelin-relayer/api/metric_detail.mdx b/content/openzeppelin-relayer/api/metric_detail.mdx new file mode 100644 index 00000000..021017d1 --- /dev/null +++ b/content/openzeppelin-relayer/api/metric_detail.mdx @@ -0,0 +1,38 @@ +--- +title: Returns the details of a specific metric in plain text format. +full: true +_openapi: + method: GET + route: /metrics/{metric_name} + toc: [] + structuredData: + headings: [] + contents: + - content: >- + # Parameters + + + - `path`: The name of the metric to retrieve details for. + + + # Returns + + + An `HttpResponse` containing the metric details in plain text, or a + 404 error if the metric is + + not found. +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + +# Parameters + +- `path`: The name of the metric to retrieve details for. + +# Returns + +An `HttpResponse` containing the metric details in plain text, or a 404 error if the metric is +not found. + + \ No newline at end of file diff --git a/content/openzeppelin-relayer/api/replaceTransaction.mdx b/content/openzeppelin-relayer/api/replaceTransaction.mdx new file mode 100644 index 00000000..cc6c5d0e --- /dev/null +++ b/content/openzeppelin-relayer/api/replaceTransaction.mdx @@ -0,0 +1,15 @@ +--- +title: Replaces a specific transaction with a new one. +full: true +_openapi: + method: PUT + route: /api/v1/relayers/{relayer_id}/transactions/{transaction_id} + toc: [] + structuredData: + headings: [] + contents: [] +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/content/openzeppelin-relayer/api/rpc.mdx b/content/openzeppelin-relayer/api/rpc.mdx new file mode 100644 index 00000000..3dbb0922 --- /dev/null +++ b/content/openzeppelin-relayer/api/rpc.mdx @@ -0,0 +1,15 @@ +--- +title: Performs a JSON-RPC call using the specified relayer. +full: true +_openapi: + method: POST + route: /api/v1/relayers/{relayer_id}/rpc + toc: [] + structuredData: + headings: [] + contents: [] +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/content/openzeppelin-relayer/api/scrape_metrics.mdx b/content/openzeppelin-relayer/api/scrape_metrics.mdx new file mode 100644 index 00000000..9b43e8c4 --- /dev/null +++ b/content/openzeppelin-relayer/api/scrape_metrics.mdx @@ -0,0 +1,30 @@ +--- +title: >- + Triggers an update of system metrics and returns the result in plain text + format. +full: true +_openapi: + method: GET + route: /debug/metrics/scrape + toc: [] + structuredData: + headings: [] + contents: + - content: >- + # Returns + + + An `HttpResponse` containing the updated metrics in plain text, or an + error message if the + + update fails. +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + +# Returns + +An `HttpResponse` containing the updated metrics in plain text, or an error message if the +update fails. + + \ No newline at end of file diff --git a/content/openzeppelin-relayer/api/sendTransaction.mdx b/content/openzeppelin-relayer/api/sendTransaction.mdx new file mode 100644 index 00000000..086a1322 --- /dev/null +++ b/content/openzeppelin-relayer/api/sendTransaction.mdx @@ -0,0 +1,15 @@ +--- +title: Sends a transaction through the specified relayer. +full: true +_openapi: + method: POST + route: /api/v1/relayers/{relayer_id}/transactions + toc: [] + structuredData: + headings: [] + contents: [] +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/content/openzeppelin-relayer/api/sign.mdx b/content/openzeppelin-relayer/api/sign.mdx new file mode 100644 index 00000000..73a6e67d --- /dev/null +++ b/content/openzeppelin-relayer/api/sign.mdx @@ -0,0 +1,15 @@ +--- +title: Signs data using the specified relayer. +full: true +_openapi: + method: POST + route: /api/v1/relayers/{relayer_id}/sign + toc: [] + structuredData: + headings: [] + contents: [] +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/content/openzeppelin-relayer/api/signTransaction.mdx b/content/openzeppelin-relayer/api/signTransaction.mdx new file mode 100644 index 00000000..b95f49c6 --- /dev/null +++ b/content/openzeppelin-relayer/api/signTransaction.mdx @@ -0,0 +1,15 @@ +--- +title: Signs a transaction using the specified relayer (Stellar only). +full: true +_openapi: + method: POST + route: /api/v1/relayers/{relayer_id}/sign-transaction + toc: [] + structuredData: + headings: [] + contents: [] +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/content/openzeppelin-relayer/api/signTypedData.mdx b/content/openzeppelin-relayer/api/signTypedData.mdx new file mode 100644 index 00000000..edb7b736 --- /dev/null +++ b/content/openzeppelin-relayer/api/signTypedData.mdx @@ -0,0 +1,15 @@ +--- +title: Signs typed data using the specified relayer. +full: true +_openapi: + method: POST + route: /api/v1/relayers/{relayer_id}/sign-typed-data + toc: [] + structuredData: + headings: [] + contents: [] +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/content/openzeppelin-relayer/api/updateNotification.mdx b/content/openzeppelin-relayer/api/updateNotification.mdx new file mode 100644 index 00000000..b591c02f --- /dev/null +++ b/content/openzeppelin-relayer/api/updateNotification.mdx @@ -0,0 +1,15 @@ +--- +title: Updates an existing notification. +full: true +_openapi: + method: PATCH + route: /api/v1/notifications/{notification_id} + toc: [] + structuredData: + headings: [] + contents: [] +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/content/openzeppelin-relayer/api/updateRelayer.mdx b/content/openzeppelin-relayer/api/updateRelayer.mdx new file mode 100644 index 00000000..9bd2ea5e --- /dev/null +++ b/content/openzeppelin-relayer/api/updateRelayer.mdx @@ -0,0 +1,15 @@ +--- +title: Updates a relayer's information based on the provided update request. +full: true +_openapi: + method: PATCH + route: /api/v1/relayers/{relayer_id} + toc: [] + structuredData: + headings: [] + contents: [] +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/content/openzeppelin-relayer/api/updateSigner.mdx b/content/openzeppelin-relayer/api/updateSigner.mdx new file mode 100644 index 00000000..080f7f4c --- /dev/null +++ b/content/openzeppelin-relayer/api/updateSigner.mdx @@ -0,0 +1,15 @@ +--- +title: Updates an existing signer. +full: true +_openapi: + method: PATCH + route: /api/v1/signers/{signer_id} + toc: [] + structuredData: + headings: [] + contents: [] +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/package.json b/package.json index fe169505..c3a74580 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "clsx": "^2.1.1", "fumadocs-core": "15.7.7", "fumadocs-mdx": "11.8.2", + "fumadocs-openapi": "^9.3.7", "fumadocs-ui": "15.7.7", "glob": "^11.0.3", "katex": "^0.16.22", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 71b926b6..c83773ad 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -29,6 +29,9 @@ importers: fumadocs-mdx: specifier: 11.8.2 version: 11.8.2(@fumadocs/mdx-remote@1.4.0(@types/react@19.1.12)(fumadocs-core@15.7.7(@types/react@19.1.12)(next@15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1))(fumadocs-core@15.7.7(@types/react@19.1.12)(next@15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(next@15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1) + fumadocs-openapi: + specifier: ^9.3.7 + version: 9.3.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(next@15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(tailwindcss@4.1.12) fumadocs-ui: specifier: 15.7.7 version: 15.7.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(next@15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(tailwindcss@4.1.12) @@ -112,6 +115,10 @@ packages: '@antfu/utils@8.1.1': resolution: {integrity: sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ==} + '@apidevtools/json-schema-ref-parser@11.9.3': + resolution: {integrity: sha512-60vepv88RwcJtSHrD6MjIL6Ta3SOYbgfnkHb+ppAVK+o9mXprRtulx7VlRl3lN3bbvysAfCS7WMVfhUYemB0IQ==} + engines: {node: '>= 16'} + '@biomejs/biome@2.2.2': resolution: {integrity: sha512-j1omAiQWCkhuLgwpMKisNKnsM6W8Xtt1l0WZmqY/dFj8QPNkIoTvk4tSsi40FaAAkBE1PU0AFG2RWFBWenAn+w==} engines: {node: '>=14.21.3'} @@ -370,6 +377,10 @@ packages: '@types/react': optional: true + '@fumari/json-schema-to-typescript@1.1.3': + resolution: {integrity: sha512-KnaZAo5W769nOaxhPqEMTdjHdngugxmPpNS+Yr2U90iVxgmNAWwhSr8Nx3l+CUehJKNFzJi2C7clQXOfuPJegA==} + engines: {node: '>=18.0.0'} + '@iconify/types@2.0.0': resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} @@ -530,6 +541,9 @@ packages: '@jridgewell/trace-mapping@0.3.30': resolution: {integrity: sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==} + '@jsdevtools/ono@7.1.3': + resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==} + '@mdx-js/mdx@3.1.1': resolution: {integrity: sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==} @@ -595,6 +609,10 @@ packages: resolution: {integrity: sha512-U7PY8FwXHuJ6bNBpbsqe0KLzb91IcJuORDggqHHkFy1waokY5SpWLN9tzB3AOW776awp6s1bjwts9I9Davy3lw==} engines: {node: '>= 20.0.0'} + '@orama/orama@3.1.13': + resolution: {integrity: sha512-O0hdKt4K31i8fpq8Bw5RfdPVAqm0EdduBUcluPo2MRcfCOwUEf5JlnvRhf/J0ezOYOD8jQ/LumYZxOVi/XK/BA==} + engines: {node: '>= 20.0.0'} + '@radix-ui/number@1.1.1': resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==} @@ -841,6 +859,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-select@2.2.6': + resolution: {integrity: sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-separator@1.1.7': resolution: {integrity: sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA==} peerDependencies: @@ -964,6 +995,14 @@ packages: '@radix-ui/rect@1.1.1': resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} + '@scalar/openapi-parser@0.18.3': + resolution: {integrity: sha512-j0OM3Y3JTVx50m7r5ogoADe2pT/ewhAnO7knjkcm7b+vjgKRpgjUMhXsYraBemFkqd2dNa1QfTX/mDVv7wEr9A==} + engines: {node: '>=20'} + + '@scalar/openapi-types@0.3.7': + resolution: {integrity: sha512-QHSvHBVDze3+dUwAhIGq6l1iOev4jdoqdBK7QpfeN1Q4h+6qpVEw3EEqBiH0AXUSh/iWwObBv4uMgfIx0aNZ5g==} + engines: {node: '>=20'} + '@shikijs/core@3.12.1': resolution: {integrity: sha512-j9+UDQ6M50xvaSR/e9lg212H0Fqxy3lYd39Q6YITYQxfrb5VYNUKPLZp4PN9f+YmRcdpyNAm3obn/tIZ2WkUWg==} @@ -991,6 +1030,9 @@ packages: '@shikijs/rehype@3.12.1': resolution: {integrity: sha512-MEWt7qNyvlzVsQAf6WWTX+EN5IxRW3VUfzxrmQQHOUXjGePjQcVpHuaapW/BxgUX5hNhXKUtBVR7KMwY3ASLjA==} + '@shikijs/rehype@3.12.2': + resolution: {integrity: sha512-9wg+FKv0ByaQScTonpZdrDhADOoJP/yCWLAuiYYG6GehwNV5rGwnLvWKj33UmtLedKMSHzWUdB+Un6rfDFo/FA==} + '@shikijs/themes@3.12.1': resolution: {integrity: sha512-9JrAm9cA5hqM/YXymA3oAAZdnCgQf1zyrNDtsnM105nNEoEpux4dyzdoOjc2KawEKj1iUs/WH2ota6Atp7GYkQ==} @@ -1000,6 +1042,9 @@ packages: '@shikijs/transformers@3.12.1': resolution: {integrity: sha512-crGh3cSZf6mwg3K2W8i79Ja+q4tVClRHdHLnUGi5arS58+cqdzsbkrEZBDMyevf9ehmjFUWDTEwCMEyp9I3z0g==} + '@shikijs/transformers@3.12.2': + resolution: {integrity: sha512-+z1aMq4N5RoNGY8i7qnTYmG2MBYzFmwkm/yOd6cjEI7OVzcldVvzQCfxU1YbIVgsyB0xHVc2jFe1JhgoXyUoSQ==} + '@shikijs/types@3.12.1': resolution: {integrity: sha512-Is/p+1vTss22LIsGCJTmGrxu7ZC1iBL9doJFYLaZ4aI8d0VDXb7Mn0kBzhkc7pdsRpmUbQLQ5HXwNpa3H6F8og==} @@ -1211,6 +1256,9 @@ packages: '@types/hast@3.0.4': resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + '@types/katex@0.16.7': resolution: {integrity: sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==} @@ -1256,6 +1304,25 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + ajv-draft-04@1.0.0: + resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} + peerDependencies: + ajv: ^8.5.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv-formats@3.0.1: + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv@8.17.1: + resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -1650,6 +1717,16 @@ packages: extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-uri@3.1.0: + resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + + fast-xml-parser@4.5.3: + resolution: {integrity: sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==} + hasBin: true + fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -1659,10 +1736,48 @@ packages: picomatch: optional: true + foreach@2.0.6: + resolution: {integrity: sha512-k6GAGDyqLe9JaebCsFCoudPPWfihKu8pylYXRlqP1J7ms39iPoTtk2fviNglIeQEwdh0bQeKJ01ZPyuyQvKzwg==} + foreground-child@3.3.1: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} + fumadocs-core@15.7.10: + resolution: {integrity: sha512-QQVj57Y6GWI+fpfD/YZ3uhay5i13OBVY1+S6dKn4c4M25v2WL7mjTpWM8VwdZOSu4PETQM7/qrTzDILQFMGV3w==} + peerDependencies: + '@mixedbread/sdk': ^0.19.0 + '@oramacloud/client': 1.x.x || 2.x.x + '@tanstack/react-router': 1.x.x + '@types/react': '*' + algoliasearch: 5.x.x + next: 14.x.x || 15.x.x + react: 18.x.x || 19.x.x + react-dom: 18.x.x || 19.x.x + react-router: 7.x.x + waku: ^0.26.0 + peerDependenciesMeta: + '@mixedbread/sdk': + optional: true + '@oramacloud/client': + optional: true + '@tanstack/react-router': + optional: true + '@types/react': + optional: true + algoliasearch: + optional: true + next: + optional: true + react: + optional: true + react-dom: + optional: true + react-router: + optional: true + waku: + optional: true + fumadocs-core@15.7.7: resolution: {integrity: sha512-4mo8y1L2VV9TcrQ1gses3c5zzCaPwDPYjfrPET4Qf+m7GPOqZ7wiUeXMTYb98T+N5wS0G/fsr/xFPZkgwD44gQ==} peerDependencies: @@ -1711,6 +1826,35 @@ packages: vite: optional: true + fumadocs-openapi@9.3.7: + resolution: {integrity: sha512-55+wqgX4nPcDIO9zR+W16yXwpFiDz8xp7dKdmiYlemEEaxxxtSYM+/46UWOi7Od4C54V3QDj92BWIiVO7OafzQ==} + peerDependencies: + '@scalar/api-client-react': '*' + '@types/react': '*' + react: 18.x.x || 19.x.x + react-dom: 18.x.x || 19.x.x + peerDependenciesMeta: + '@scalar/api-client-react': + optional: true + '@types/react': + optional: true + + fumadocs-ui@15.7.10: + resolution: {integrity: sha512-yGSCuRwFSiHFbVperUzSM+8aP7LbOzI3HoiX1ORDPI1DsuUE1nCyxHjnKsczZImhjEwOqJPf/gywZHAbiPTJkA==} + peerDependencies: + '@types/react': '*' + next: 14.x.x || 15.x.x + react: 18.x.x || 19.x.x + react-dom: 18.x.x || 19.x.x + tailwindcss: ^3.4.14 || ^4.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + next: + optional: true + tailwindcss: + optional: true + fumadocs-ui@15.7.7: resolution: {integrity: sha512-nXeEnFI0h+JAbwWsKWcc6aBuR++jWlxhMpXQnPv4zbrrbds436lilrOu/xh5KxPiEe2M9HspKMN+Oee73jHQFw==} peerDependencies: @@ -1860,6 +2004,16 @@ packages: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true + json-pointer@0.6.2: + resolution: {integrity: sha512-vLWcKbOaXlO+jvRy4qNd+TI1QUPZzfJj1tpJ3vAXDych5XJf93ftpUKe5pKCrzyIIwgBJcOcCVRUfqQP25afBw==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + jsonpointer@5.0.1: + resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} + engines: {node: '>=0.10.0'} + katex@0.16.22: resolution: {integrity: sha512-XCHRdUw4lf3SKBaJe4EvgqIuWwkPSo9XoeO8GjQW94Bp7TWv9hNhzZjZ+OH9yf1UmLygb7DIT5GSFQiyt16zYg==} hasBin: true @@ -1884,6 +2038,10 @@ packages: layout-base@2.0.1: resolution: {integrity: sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==} + leven@4.0.0: + resolution: {integrity: sha512-puehA3YKku3osqPlNuzGDUHq8WpwXupUg1V6NXdV38G+gr+gkBwFC8g1b/+YcIvp8gnqVIus+eJCH/eGsRmJNw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + lightningcss-darwin-arm64@1.30.1: resolution: {integrity: sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==} engines: {node: '>= 12.0.0'} @@ -2216,6 +2374,9 @@ packages: oniguruma-to-es@4.3.3: resolution: {integrity: sha512-rPiZhzC3wXwE59YQMRDodUwwT9FZ9nNBwQQfsd1wfdtlKEyCdRV0avrTcSZ5xlIvGRVPd/cx6ZN45ECmS39xvg==} + openapi-sampler@1.6.1: + resolution: {integrity: sha512-s1cIatOqrrhSj2tmJ4abFYZQK6l5v+V4toO5q1Pa0DyN8mtyqy2I+Qrj5W9vOELEtybIMQs/TBZGVO/DtTFK8w==} + package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} @@ -2273,6 +2434,11 @@ packages: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} + prettier@3.6.2: + resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} + engines: {node: '>=14'} + hasBin: true + property-information@7.1.0: resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} @@ -2284,6 +2450,12 @@ packages: peerDependencies: react: ^19.1.1 + react-hook-form@7.62.0: + resolution: {integrity: sha512-7KWFejc98xqG/F4bAxpL41NB3o1nnvQO1RWZT3TqRZYL8RryQETGfEdVnJN2fy1crCiBLLjkRBVK05j24FxJGA==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^16.8.0 || ^17 || ^18 || ^19 + react-medium-image-zoom@5.3.0: resolution: {integrity: sha512-RCIzVlsKqy3BYgGgYbolUfuvx0aSKC7YhX/IJGEp+WJxsqdIVYJHkBdj++FAj6VD7RiWj6VVmdCfa/9vJE9hZg==} peerDependencies: @@ -2378,6 +2550,10 @@ packages: remark@15.0.1: resolution: {integrity: sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A==} + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + robust-predicates@3.0.2: resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==} @@ -2390,6 +2566,9 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + sax@1.4.1: + resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} + scheduler@0.26.0: resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} @@ -2467,6 +2646,9 @@ packages: resolution: {integrity: sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==} engines: {node: '>=0.10.0'} + strnum@1.1.2: + resolution: {integrity: sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==} + style-to-js@1.1.17: resolution: {integrity: sha512-xQcBGDxJb6jjFCTzvQtfiPn6YvvP2O8U1MDIPNfJQlWMYfktPy+iGsHE7cssjs7y84d9fQaK4UF3RIJaAHSoYA==} @@ -2636,10 +2818,22 @@ packages: resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} engines: {node: '>=12'} + xml-js@1.6.11: + resolution: {integrity: sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==} + hasBin: true + yallist@5.0.0: resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} engines: {node: '>=18'} + yaml@2.8.0: + resolution: {integrity: sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==} + engines: {node: '>= 14.6'} + hasBin: true + + zod@3.24.1: + resolution: {integrity: sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==} + zod@4.1.5: resolution: {integrity: sha512-rcUUZqlLJgBC33IT3PNMgsCq6TzLQEG/Ei/KTCU0PedSWRMAXoOUN+4t/0H+Q8bdnLPdqUYnvboJT0bn/229qg==} @@ -2657,6 +2851,12 @@ snapshots: '@antfu/utils@8.1.1': {} + '@apidevtools/json-schema-ref-parser@11.9.3': + dependencies: + '@jsdevtools/ono': 7.1.3 + '@types/json-schema': 7.0.15 + js-yaml: 4.1.0 + '@biomejs/biome@2.2.2': optionalDependencies: '@biomejs/cli-darwin-arm64': 2.2.2 @@ -2827,6 +3027,12 @@ snapshots: transitivePeerDependencies: - supports-color + '@fumari/json-schema-to-typescript@1.1.3': + dependencies: + '@apidevtools/json-schema-ref-parser': 11.9.3 + js-yaml: 4.1.0 + prettier: 3.6.2 + '@iconify/types@2.0.0': {} '@iconify/utils@2.3.0': @@ -2966,6 +3172,8 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@jsdevtools/ono@7.1.3': {} + '@mdx-js/mdx@3.1.1': dependencies: '@types/estree': 1.0.8 @@ -3030,6 +3238,8 @@ snapshots: '@orama/orama@3.1.12': {} + '@orama/orama@3.1.13': {} + '@radix-ui/number@1.1.1': {} '@radix-ui/primitive@1.1.3': {} @@ -3291,6 +3501,35 @@ snapshots: '@types/react': 19.1.12 '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-select@2.2.6(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@radix-ui/number': 1.1.1 + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@19.1.1) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@19.1.1) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.12)(react@19.1.1) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.1.12)(react@19.1.1) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.12)(react@19.1.1) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.12)(react@19.1.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.12)(react@19.1.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.12)(react@19.1.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.12)(react@19.1.1) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.12)(react@19.1.1) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + aria-hidden: 1.2.6 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + react-remove-scroll: 2.7.1(@types/react@19.1.12)(react@19.1.1) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-separator@1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) @@ -3388,6 +3627,20 @@ snapshots: '@radix-ui/rect@1.1.1': {} + '@scalar/openapi-parser@0.18.3': + dependencies: + '@scalar/openapi-types': 0.3.7 + ajv: 8.17.1 + ajv-draft-04: 1.0.0(ajv@8.17.1) + ajv-formats: 3.0.1(ajv@8.17.1) + jsonpointer: 5.0.1 + leven: 4.0.0 + yaml: 2.8.0 + + '@scalar/openapi-types@0.3.7': + dependencies: + zod: 3.24.1 + '@shikijs/core@3.12.1': dependencies: '@shikijs/types': 3.12.1 @@ -3441,6 +3694,15 @@ snapshots: unified: 11.0.5 unist-util-visit: 5.0.0 + '@shikijs/rehype@3.12.2': + dependencies: + '@shikijs/types': 3.12.2 + '@types/hast': 3.0.4 + hast-util-to-string: 3.0.1 + shiki: 3.12.2 + unified: 11.0.5 + unist-util-visit: 5.0.0 + '@shikijs/themes@3.12.1': dependencies: '@shikijs/types': 3.12.1 @@ -3454,6 +3716,11 @@ snapshots: '@shikijs/core': 3.12.1 '@shikijs/types': 3.12.1 + '@shikijs/transformers@3.12.2': + dependencies: + '@shikijs/core': 3.12.2 + '@shikijs/types': 3.12.2 + '@shikijs/types@3.12.1': dependencies: '@shikijs/vscode-textmate': 10.0.2 @@ -3677,6 +3944,8 @@ snapshots: dependencies: '@types/unist': 3.0.3 + '@types/json-schema@7.0.15': {} + '@types/katex@0.16.7': {} '@types/mdast@4.0.4': @@ -3714,6 +3983,21 @@ snapshots: acorn@8.15.0: {} + ajv-draft-04@1.0.0(ajv@8.17.1): + optionalDependencies: + ajv: 8.17.1 + + ajv-formats@3.0.1(ajv@8.17.1): + optionalDependencies: + ajv: 8.17.1 + + ajv@8.17.1: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.0 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + ansi-regex@5.0.1: {} ansi-regex@6.2.0: {} @@ -4145,15 +4429,52 @@ snapshots: extend@3.0.2: {} + fast-deep-equal@3.1.3: {} + + fast-uri@3.1.0: {} + + fast-xml-parser@4.5.3: + dependencies: + strnum: 1.1.2 + fdir@6.5.0(picomatch@4.0.3): optionalDependencies: picomatch: 4.0.3 + foreach@2.0.6: {} + foreground-child@3.3.1: dependencies: cross-spawn: 7.0.6 signal-exit: 4.1.0 + fumadocs-core@15.7.10(@types/react@19.1.12)(next@15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + dependencies: + '@formatjs/intl-localematcher': 0.6.1 + '@orama/orama': 3.1.13 + '@shikijs/rehype': 3.12.2 + '@shikijs/transformers': 3.12.2 + github-slugger: 2.0.0 + hast-util-to-estree: 3.1.3 + hast-util-to-jsx-runtime: 2.3.6 + image-size: 2.0.2 + negotiator: 1.0.0 + npm-to-yarn: 3.0.1 + react-remove-scroll: 2.7.1(@types/react@19.1.12)(react@19.1.1) + remark: 15.0.1 + remark-gfm: 4.0.1 + remark-rehype: 11.1.2 + scroll-into-view-if-needed: 3.1.0 + shiki: 3.12.2 + unist-util-visit: 5.0.0 + optionalDependencies: + '@types/react': 19.1.12 + next: 15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + transitivePeerDependencies: + - supports-color + fumadocs-core@15.7.7(@types/react@19.1.12)(next@15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1): dependencies: '@formatjs/intl-localematcher': 0.6.1 @@ -4206,6 +4527,82 @@ snapshots: transitivePeerDependencies: - supports-color + fumadocs-openapi@9.3.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(next@15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(tailwindcss@4.1.12): + dependencies: + '@fumari/json-schema-to-typescript': 1.1.3 + '@radix-ui/react-accordion': 1.2.12(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-select': 2.2.6(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.12)(react@19.1.1) + '@scalar/openapi-parser': 0.18.3 + ajv: 8.17.1 + class-variance-authority: 0.7.1 + fumadocs-core: 15.7.10(@types/react@19.1.12)(next@15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + fumadocs-ui: 15.7.10(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(next@15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(tailwindcss@4.1.12) + github-slugger: 2.0.0 + gray-matter: 4.0.3 + hast-util-to-jsx-runtime: 2.3.6 + js-yaml: 4.1.0 + next-themes: 0.4.6(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + openapi-sampler: 1.6.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + react-hook-form: 7.62.0(react@19.1.1) + remark: 15.0.1 + remark-rehype: 11.1.2 + shiki: 3.12.2 + tinyglobby: 0.2.14 + xml-js: 1.6.11 + optionalDependencies: + '@types/react': 19.1.12 + transitivePeerDependencies: + - '@mixedbread/sdk' + - '@oramacloud/client' + - '@tanstack/react-router' + - '@types/react-dom' + - algoliasearch + - next + - react-router + - supports-color + - tailwindcss + - waku + + fumadocs-ui@15.7.10(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(next@15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(tailwindcss@4.1.12): + dependencies: + '@radix-ui/react-accordion': 1.2.12(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.12)(react@19.1.1) + '@radix-ui/react-navigation-menu': 1.2.14(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-popover': 1.1.15(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-scroll-area': 1.2.10(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.12)(react@19.1.1) + '@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + class-variance-authority: 0.7.1 + fumadocs-core: 15.7.10(@types/react@19.1.12)(next@15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + lodash.merge: 4.6.2 + next-themes: 0.4.6(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + postcss-selector-parser: 7.1.0 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + react-medium-image-zoom: 5.3.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + scroll-into-view-if-needed: 3.1.0 + tailwind-merge: 3.3.1 + optionalDependencies: + '@types/react': 19.1.12 + next: 15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + tailwindcss: 4.1.12 + transitivePeerDependencies: + - '@mixedbread/sdk' + - '@oramacloud/client' + - '@tanstack/react-router' + - '@types/react-dom' + - algoliasearch + - react-router + - supports-color + - waku + fumadocs-ui@15.7.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(next@15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(tailwindcss@4.1.12): dependencies: '@radix-ui/react-accordion': 1.2.12(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) @@ -4436,6 +4833,14 @@ snapshots: dependencies: argparse: 2.0.1 + json-pointer@0.6.2: + dependencies: + foreach: 2.0.6 + + json-schema-traverse@1.0.0: {} + + jsonpointer@5.0.1: {} + katex@0.16.22: dependencies: commander: 8.3.0 @@ -4458,6 +4863,8 @@ snapshots: layout-base@2.0.1: {} + leven@4.0.0: {} + lightningcss-darwin-arm64@1.30.1: optional: true @@ -5068,6 +5475,12 @@ snapshots: regex: 6.0.1 regex-recursion: 6.0.2 + openapi-sampler@1.6.1: + dependencies: + '@types/json-schema': 7.0.15 + fast-xml-parser: 4.5.3 + json-pointer: 0.6.2 + package-json-from-dist@1.0.1: {} package-manager-detector@1.3.0: {} @@ -5137,6 +5550,8 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + prettier@3.6.2: {} + property-information@7.1.0: {} quansync@0.2.11: {} @@ -5146,6 +5561,10 @@ snapshots: react: 19.1.1 scheduler: 0.26.0 + react-hook-form@7.62.0(react@19.1.1): + dependencies: + react: 19.1.1 + react-medium-image-zoom@5.3.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1): dependencies: react: 19.1.1 @@ -5298,6 +5717,8 @@ snapshots: transitivePeerDependencies: - supports-color + require-from-string@2.0.2: {} + robust-predicates@3.0.2: {} roughjs@4.6.6: @@ -5311,6 +5732,8 @@ snapshots: safer-buffer@2.1.2: {} + sax@1.4.1: {} + scheduler@0.26.0: {} scroll-into-view-if-needed@3.1.0: @@ -5425,6 +5848,8 @@ snapshots: strip-bom-string@1.0.0: {} + strnum@1.1.2: {} + style-to-js@1.1.17: dependencies: style-to-object: 1.0.9 @@ -5594,8 +6019,16 @@ snapshots: string-width: 5.1.2 strip-ansi: 7.1.0 + xml-js@1.6.11: + dependencies: + sax: 1.4.1 + yallist@5.0.0: {} + yaml@2.8.0: {} + + zod@3.24.1: {} + zod@4.1.5: {} zwitch@2.0.4: {} diff --git a/scripts/generate-openapi-docs.js b/scripts/generate-openapi-docs.js new file mode 100644 index 00000000..14c2be1c --- /dev/null +++ b/scripts/generate-openapi-docs.js @@ -0,0 +1,10 @@ +import { generateFiles } from "fumadocs-openapi"; +import { openapi } from "@/lib/openapi"; + +void generateFiles({ + input: openapi, + output: "./content/openzeppelin-relayer/api", + // we recommend to enable it + // make sure your endpoint description doesn't break MDX syntax. + includeDescription: true, +}); diff --git a/src/app/global.css b/src/app/global.css index 80fd28a1..52b8858c 100644 --- a/src/app/global.css +++ b/src/app/global.css @@ -3,6 +3,7 @@ @import "fumadocs-ui/css/preset.css"; @import "katex/dist/katex.css"; @import "tw-animate-css"; +@import "fumadocs-openapi/css/preset.css"; @custom-variant dark (&:is(.dark *)); diff --git a/src/lib/openapi.ts b/src/lib/openapi.ts new file mode 100644 index 00000000..8bfcb7ae --- /dev/null +++ b/src/lib/openapi.ts @@ -0,0 +1,8 @@ +import { createOpenAPI } from "fumadocs-openapi/server"; + +export const openapi = createOpenAPI({ + // the OpenAPI schema, you can also give it an external URL. + input: [ + "https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-relayer/refs/heads/main/docs/openapi.json", + ], +}); diff --git a/src/lib/source.ts b/src/lib/source.ts index ebf6913c..ee726268 100644 --- a/src/lib/source.ts +++ b/src/lib/source.ts @@ -2,10 +2,14 @@ import { loader } from "fumadocs-core/source"; import { icons } from "lucide-react"; import { createElement } from "react"; import { docs } from "@/.source"; +import { transformerOpenAPI } from "fumadocs-openapi/server"; // See https://fumadocs.vercel.app/docs/headless/source-api for more info export const source = loader({ // it assigns a URL to your pages + pageTree: { + transformers: [transformerOpenAPI()], + }, baseUrl: "/", source: docs.toFumadocsSource(), icon(icon) { diff --git a/src/mdx-components.tsx b/src/mdx-components.tsx index 026ac9a2..6a17d5f1 100644 --- a/src/mdx-components.tsx +++ b/src/mdx-components.tsx @@ -18,6 +18,8 @@ import { import type { MDXComponents } from "mdx/types"; import { Mermaid } from "@/components/mdx/mermaid"; import OZWizard from "./components/oz-wizard"; +import { APIPage } from "fumadocs-openapi/ui"; +import { openapi } from "@/lib/openapi"; // use this function to get MDX components, you will need it for rendering MDX export function getMDXComponents(components?: MDXComponents): MDXComponents { @@ -39,6 +41,7 @@ export function getMDXComponents(components?: MDXComponents): MDXComponents { RefreshCwIcon, PlugIcon, Mermaid, + APIPage: (props) => , ...components, }; } diff --git a/src/navigation/ethereum-evm.json b/src/navigation/ethereum-evm.json index 3ebffa0d..94df888f 100644 --- a/src/navigation/ethereum-evm.json +++ b/src/navigation/ethereum-evm.json @@ -996,10 +996,175 @@ "url": "/openzeppelin-relayer/stellar" }, { - "type": "page", + "type": "folder", "name": "API Reference", - "url": "https://release-v1-1-0--openzeppelin-relayer.netlify.app/api_docs.html", - "external": true + "children": [ + { + "type": "page", + "name": "Call Plugin", + "url": "/openzeppelin-relayer/api/callPlugin" + }, + { + "type": "page", + "name": "Cancel Transaction", + "url": "/openzeppelin-relayer/api/cancelTransaction" + }, + { + "type": "page", + "name": "Create Notification", + "url": "/openzeppelin-relayer/api/createNotification" + }, + { + "type": "page", + "name": "Create Relayer", + "url": "/openzeppelin-relayer/api/createRelayer" + }, + { + "type": "page", + "name": "Create Signer", + "url": "/openzeppelin-relayer/api/createSigner" + }, + { + "type": "page", + "name": "Delete Notification", + "url": "/openzeppelin-relayer/api/deleteNotification" + }, + { + "type": "page", + "name": "Delete Pending Transactions", + "url": "/openzeppelin-relayer/api/deletePendingTransactions" + }, + { + "type": "page", + "name": "Delete Relayer", + "url": "/openzeppelin-relayer/api/deleteRelayer" + }, + { + "type": "page", + "name": "Delete Signer", + "url": "/openzeppelin-relayer/api/deleteSigner" + }, + { + "type": "page", + "name": "Get Notification", + "url": "/openzeppelin-relayer/api/getNotification" + }, + { + "type": "page", + "name": "Get Relayer", + "url": "/openzeppelin-relayer/api/getRelayer" + }, + { + "type": "page", + "name": "Get Relayer Balance", + "url": "/openzeppelin-relayer/api/getRelayerBalance" + }, + { + "type": "page", + "name": "Get Relayer Status", + "url": "/openzeppelin-relayer/api/getRelayerStatus" + }, + { + "type": "page", + "name": "Get Signer", + "url": "/openzeppelin-relayer/api/getSigner" + }, + { + "type": "page", + "name": "Get Transaction by ID", + "url": "/openzeppelin-relayer/api/getTransactionById" + }, + { + "type": "page", + "name": "Get Transaction by Nonce", + "url": "/openzeppelin-relayer/api/getTransactionByNonce" + }, + { + "type": "page", + "name": "Health", + "url": "/openzeppelin-relayer/api/health" + }, + { + "type": "page", + "name": "List Notifications", + "url": "/openzeppelin-relayer/api/listNotifications" + }, + { + "type": "page", + "name": "List Relayers", + "url": "/openzeppelin-relayer/api/listRelayers" + }, + { + "type": "page", + "name": "List Signers", + "url": "/openzeppelin-relayer/api/listSigners" + }, + { + "type": "page", + "name": "List Transactions", + "url": "/openzeppelin-relayer/api/listTransactions" + }, + { + "type": "page", + "name": "List Metrics", + "url": "/openzeppelin-relayer/api/list_metrics" + }, + { + "type": "page", + "name": "Metric Detail", + "url": "/openzeppelin-relayer/api/metric_detail" + }, + { + "type": "page", + "name": "Replace Transaction", + "url": "/openzeppelin-relayer/api/replaceTransaction" + }, + { + "type": "page", + "name": "RPC", + "url": "/openzeppelin-relayer/api/rpc" + }, + { + "type": "page", + "name": "Scrape Metrics", + "url": "/openzeppelin-relayer/api/scrape_metrics" + }, + { + "type": "page", + "name": "Send Transaction", + "url": "/openzeppelin-relayer/api/sendTransaction" + }, + { + "type": "page", + "name": "Sign", + "url": "/openzeppelin-relayer/api/sign" + }, + { + "type": "page", + "name": "Sign Transaction", + "url": "/openzeppelin-relayer/api/signTransaction" + }, + { + "type": "page", + "name": "Sign Typed Data", + "url": "/openzeppelin-relayer/api/signTypedData" + }, + { + "type": "page", + "name": "Update Notification", + "url": "/openzeppelin-relayer/api/updateNotification" + }, + { + "type": "page", + "name": "Update Relayer", + "url": "/openzeppelin-relayer/api/updateRelayer" + }, + { + "type": "page", + "name": "Update Signer", + "url": "/openzeppelin-relayer/api/updateSigner" + } + ] }, { "type": "page", @@ -1023,7 +1188,7 @@ }, { "type": "page", - "name": "Technical Rust Documentation", + "name": "Rust Book", "url": "https://release-v1-1-0--openzeppelin-relayer.netlify.app/openzeppelin_relayer/", "external": true } diff --git a/src/navigation/tools.json b/src/navigation/tools.json index 78e4701a..bf765243 100644 --- a/src/navigation/tools.json +++ b/src/navigation/tools.json @@ -384,10 +384,175 @@ "url": "/tools/openzeppelin-relayer/v1.x/stellar" }, { - "type": "page", + "type": "folder", "name": "API Reference", - "url": "https://release-v1-1-0--openzeppelin-relayer.netlify.app/api_docs.html", - "external": true + "children": [ + { + "type": "page", + "name": "Call Plugin", + "url": "/tools/openzeppelin-relayer/api/callPlugin" + }, + { + "type": "page", + "name": "Cancel Transaction", + "url": "/tools/openzeppelin-relayer/api/cancelTransaction" + }, + { + "type": "page", + "name": "Create Notification", + "url": "/tools/openzeppelin-relayer/api/createNotification" + }, + { + "type": "page", + "name": "Create Relayer", + "url": "/tools/openzeppelin-relayer/api/createRelayer" + }, + { + "type": "page", + "name": "Create Signer", + "url": "/tools/openzeppelin-relayer/api/createSigner" + }, + { + "type": "page", + "name": "Delete Notification", + "url": "/tools/openzeppelin-relayer/api/deleteNotification" + }, + { + "type": "page", + "name": "Delete Pending Transactions", + "url": "/tools/openzeppelin-relayer/api/deletePendingTransactions" + }, + { + "type": "page", + "name": "Delete Relayer", + "url": "/tools/openzeppelin-relayer/api/deleteRelayer" + }, + { + "type": "page", + "name": "Delete Signer", + "url": "/tools/openzeppelin-relayer/api/deleteSigner" + }, + { + "type": "page", + "name": "Get Notification", + "url": "/tools/openzeppelin-relayer/api/getNotification" + }, + { + "type": "page", + "name": "Get Relayer", + "url": "/tools/openzeppelin-relayer/api/getRelayer" + }, + { + "type": "page", + "name": "Get Relayer Balance", + "url": "/tools/openzeppelin-relayer/api/getRelayerBalance" + }, + { + "type": "page", + "name": "Get Relayer Status", + "url": "/tools/openzeppelin-relayer/api/getRelayerStatus" + }, + { + "type": "page", + "name": "Get Signer", + "url": "/tools/openzeppelin-relayer/api/getSigner" + }, + { + "type": "page", + "name": "Get Transaction by ID", + "url": "/tools/openzeppelin-relayer/api/getTransactionById" + }, + { + "type": "page", + "name": "Get Transaction by Nonce", + "url": "/tools/openzeppelin-relayer/api/getTransactionByNonce" + }, + { + "type": "page", + "name": "Health", + "url": "/tools/openzeppelin-relayer/api/health" + }, + { + "type": "page", + "name": "List Notifications", + "url": "/tools/openzeppelin-relayer/api/listNotifications" + }, + { + "type": "page", + "name": "List Relayers", + "url": "/tools/openzeppelin-relayer/api/listRelayers" + }, + { + "type": "page", + "name": "List Signers", + "url": "/tools/openzeppelin-relayer/api/listSigners" + }, + { + "type": "page", + "name": "List Transactions", + "url": "/tools/openzeppelin-relayer/api/listTransactions" + }, + { + "type": "page", + "name": "List Metrics", + "url": "/tools/openzeppelin-relayer/api/list_metrics" + }, + { + "type": "page", + "name": "Metric Detail", + "url": "/tools/openzeppelin-relayer/api/metric_detail" + }, + { + "type": "page", + "name": "Replace Transaction", + "url": "/tools/openzeppelin-relayer/api/replaceTransaction" + }, + { + "type": "page", + "name": "RPC", + "url": "/tools/openzeppelin-relayer/api/rpc" + }, + { + "type": "page", + "name": "Scrape Metrics", + "url": "/tools/openzeppelin-relayer/api/scrape_metrics" + }, + { + "type": "page", + "name": "Send Transaction", + "url": "/tools/openzeppelin-relayer/api/sendTransaction" + }, + { + "type": "page", + "name": "Sign", + "url": "/tools/openzeppelin-relayer/api/sign" + }, + { + "type": "page", + "name": "Sign Transaction", + "url": "/tools/openzeppelin-relayer/api/signTransaction" + }, + { + "type": "page", + "name": "Sign Typed Data", + "url": "/tools/openzeppelin-relayer/api/signTypedData" + }, + { + "type": "page", + "name": "Update Notification", + "url": "/tools/openzeppelin-relayer/api/updateNotification" + }, + { + "type": "page", + "name": "Update Relayer", + "url": "/tools/openzeppelin-relayer/api/updateRelayer" + }, + { + "type": "page", + "name": "Update Signer", + "url": "/tools/openzeppelin-relayer/api/updateSigner" + } + ] }, { "type": "page", From cfeb7a9621433e4eb79605569b618421309969cb Mon Sep 17 00:00:00 2001 From: Steve Date: Tue, 9 Sep 2025 10:49:07 -0400 Subject: [PATCH 07/14] feat: added ai components --- cli.json | 11 ++ next.config.mjs | 12 +- package.json | 3 + pnpm-lock.yaml | 9 + src/app/(docs)/[...slug]/page.tsx | 9 + src/app/llms-full.txt/route.ts | 12 ++ src/app/llms.mdx/[[...slug]]/route.ts | 21 +++ src/components/page-actions.tsx | 247 ++++++++++++++++++++++++++ src/components/ui/button.tsx | 28 +++ src/lib/cn.ts | 1 + src/lib/get-llm-text.ts | 31 ++++ 11 files changed, 382 insertions(+), 2 deletions(-) create mode 100644 cli.json create mode 100644 src/app/llms-full.txt/route.ts create mode 100644 src/app/llms.mdx/[[...slug]]/route.ts create mode 100644 src/components/page-actions.tsx create mode 100644 src/components/ui/button.tsx create mode 100644 src/lib/cn.ts create mode 100644 src/lib/get-llm-text.ts diff --git a/cli.json b/cli.json new file mode 100644 index 00000000..8a9e3361 --- /dev/null +++ b/cli.json @@ -0,0 +1,11 @@ +{ + "aliases": { + "uiDir": "./components/ui", + "componentsDir": "./components", + "blockDir": "./components", + "cssDir": "./styles", + "libDir": "./lib" + }, + "baseDir": "src", + "commands": {} +} \ No newline at end of file diff --git a/next.config.mjs b/next.config.mjs index 457dcf29..787c58e2 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -1,10 +1,18 @@ -import { createMDX } from 'fumadocs-mdx/next'; +import { createMDX } from "fumadocs-mdx/next"; const withMDX = createMDX(); /** @type {import('next').NextConfig} */ const config = { - reactStrictMode: true, + reactStrictMode: true, + async rewrites() { + return [ + { + source: "/:path*.md", + destination: "/llms.mdx/:path*", + }, + ]; + }, }; export default withMDX(config); diff --git a/package.json b/package.json index c3a74580..a1326561 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,10 @@ "react": "^19.1.1", "react-dom": "^19.1.1", "rehype-katex": "^7.0.1", + "remark": "^15.0.1", + "remark-gfm": "^4.0.1", "remark-math": "^6.0.0", + "remark-mdx": "^3.1.1", "shiki": "^3.12.2", "tailwind-merge": "^3.3.1" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c83773ad..53ff68b9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -62,9 +62,18 @@ importers: rehype-katex: specifier: ^7.0.1 version: 7.0.1 + remark: + specifier: ^15.0.1 + version: 15.0.1 + remark-gfm: + specifier: ^4.0.1 + version: 4.0.1 remark-math: specifier: ^6.0.0 version: 6.0.0 + remark-mdx: + specifier: ^3.1.1 + version: 3.1.1 shiki: specifier: ^3.12.2 version: 3.12.2 diff --git a/src/app/(docs)/[...slug]/page.tsx b/src/app/(docs)/[...slug]/page.tsx index d8d07c6b..7570b65e 100644 --- a/src/app/(docs)/[...slug]/page.tsx +++ b/src/app/(docs)/[...slug]/page.tsx @@ -8,6 +8,7 @@ import { import { notFound } from "next/navigation"; import { source } from "@/lib/source"; import { getMDXComponents } from "@/mdx-components"; +import { LLMCopyButton, ViewOptions } from "@/components/page-actions"; export default async function Page(props: { params: Promise<{ slug?: string[] }>; @@ -28,6 +29,14 @@ export default async function Page(props: { {page.data.title} {page.data.description} +
+ + + +
}, +) { + const { slug } = await params; + const page = source.getPage(slug); + if (!page) notFound(); + + return new NextResponse(await getLLMText(page)); +} + +export function generateStaticParams() { + return source.generateParams(); +} diff --git a/src/components/page-actions.tsx b/src/components/page-actions.tsx new file mode 100644 index 00000000..63ccf70c --- /dev/null +++ b/src/components/page-actions.tsx @@ -0,0 +1,247 @@ +'use client'; +import { useMemo, useState } from 'react'; +import { + Check, + ChevronDown, + Copy, + ExternalLinkIcon, + MessageCircleIcon, +} from 'lucide-react'; +import { cn } from '../lib/cn'; +import { useCopyButton } from 'fumadocs-ui/utils/use-copy-button'; +import { buttonVariants } from './ui/button'; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from 'fumadocs-ui/components/ui/popover'; +import { cva } from 'class-variance-authority'; + +const cache = new Map(); + +export function LLMCopyButton({ + /** + * A URL to fetch the raw Markdown/MDX content of page + */ + markdownUrl, +}: { + markdownUrl: string; +}) { + const [isLoading, setLoading] = useState(false); + const [checked, onClick] = useCopyButton(async () => { + const cached = cache.get(markdownUrl); + if (cached) return navigator.clipboard.writeText(cached); + + setLoading(true); + + try { + await navigator.clipboard.write([ + new ClipboardItem({ + 'text/plain': fetch(markdownUrl).then(async (res) => { + const content = await res.text(); + cache.set(markdownUrl, content); + + return content; + }), + }), + ]); + } finally { + setLoading(false); + } + }); + + return ( + + ); +} + +const optionVariants = cva( + 'text-sm p-2 rounded-lg inline-flex items-center gap-2 hover:text-fd-accent-foreground hover:bg-fd-accent [&_svg]:size-4', +); + +export function ViewOptions({ + markdownUrl, + githubUrl, +}: { + /** + * A URL to the raw Markdown/MDX content of page + */ + markdownUrl: string; + + /** + * Source file URL on GitHub + */ + githubUrl: string; +}) { + const items = useMemo(() => { + const fullMarkdownUrl = + typeof window !== 'undefined' + ? new URL(markdownUrl, window.location.origin) + : 'loading'; + const q = `Read ${fullMarkdownUrl}, I want to ask questions about it.`; + + return [ + { + title: 'Open in GitHub', + href: githubUrl, + icon: ( + + GitHub + + + ), + }, + { + title: 'Open in Scira AI', + href: `https://scira.ai/?${new URLSearchParams({ + q, + })}`, + icon: ( + + Scira AI + + + + + + + + + ), + }, + { + title: 'Open in ChatGPT', + href: `https://chatgpt.com/?${new URLSearchParams({ + hints: 'search', + q, + })}`, + icon: ( + + OpenAI + + + ), + }, + { + title: 'Open in Claude', + href: `https://claude.ai/new?${new URLSearchParams({ + q, + })}`, + icon: ( + + Anthropic + + + ), + }, + { + title: 'Open in T3 Chat', + href: `https://t3.chat/new?${new URLSearchParams({ + q, + })}`, + icon: , + }, + ]; + }, [githubUrl, markdownUrl]); + + return ( + + + Open + + + + {items.map((item) => ( + + {item.icon} + {item.title} + + + ))} + + + ); +} diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx new file mode 100644 index 00000000..5fca2adb --- /dev/null +++ b/src/components/ui/button.tsx @@ -0,0 +1,28 @@ +import { cva, type VariantProps } from 'class-variance-authority'; + +const variants = { + primary: 'bg-fd-primary text-fd-primary-foreground hover:bg-fd-primary/80', + outline: 'border hover:bg-fd-accent hover:text-fd-accent-foreground', + ghost: 'hover:bg-fd-accent hover:text-fd-accent-foreground', + secondary: + 'border bg-fd-secondary text-fd-secondary-foreground hover:bg-fd-accent hover:text-fd-accent-foreground', +} as const; + +export const buttonVariants = cva( + 'inline-flex items-center justify-center rounded-md p-2 text-sm font-medium transition-colors duration-100 disabled:pointer-events-none disabled:opacity-50 focus-visible:outline-none', + { + variants: { + variant: variants, + // fumadocs use `color` instead of `variant` + color: variants, + size: { + sm: 'gap-1 px-2 py-1.5 text-xs', + icon: 'p-1.5 [&_svg]:size-5', + 'icon-sm': 'p-1.5 [&_svg]:size-4.5', + 'icon-xs': 'p-1 [&_svg]:size-4', + }, + }, + }, +); + +export type ButtonProps = VariantProps; diff --git a/src/lib/cn.ts b/src/lib/cn.ts new file mode 100644 index 00000000..ba66fd25 --- /dev/null +++ b/src/lib/cn.ts @@ -0,0 +1 @@ +export { twMerge as cn } from 'tailwind-merge'; diff --git a/src/lib/get-llm-text.ts b/src/lib/get-llm-text.ts new file mode 100644 index 00000000..f23ffc79 --- /dev/null +++ b/src/lib/get-llm-text.ts @@ -0,0 +1,31 @@ +import { remark } from "remark"; +import remarkGfm from "remark-gfm"; +import remarkMdx from "remark-mdx"; +import { remarkInclude } from "fumadocs-mdx/config"; +import { source } from "./source"; +import type { InferPageType } from "fumadocs-core/source"; +import fs from "node:fs/promises"; + +const processor = remark() + .use(remarkMdx) + // needed for Fumadocs MDX + .use(remarkInclude) + .use(remarkGfm); + +export async function getLLMText(page: InferPageType) { + try { + const processed = await processor.process({ + path: page.absolutePath, + value: await fs.readFile(page.absolutePath), + }); + + // note: it doesn't escape frontmatter, it's up to you. + return `# ${page.data.title} +URL: ${page.url} + +${processed.value}`; + } catch (error) { + console.error(`Error processing page: ${page.absolutePath}`, error); + throw error; + } +} From 97fa3fc40e4af50ae1e6caa05c6d493f43a50595 Mon Sep 17 00:00:00 2001 From: Steve Date: Tue, 9 Sep 2025 10:50:19 -0400 Subject: [PATCH 08/14] chore: applied formatting --- src/app/(docs)/[...slug]/page.tsx | 17 +- src/app/llms-full.txt/route.ts | 2 +- src/app/llms.mdx/[[...slug]]/route.ts | 22 +- src/components/page-actions.tsx | 447 +++++++++++++------------- src/components/ui/button.tsx | 40 +-- src/lib/cn.ts | 2 +- src/lib/get-llm-text.ts | 8 +- src/lib/source.ts | 2 +- src/mdx-components.tsx | 4 +- 9 files changed, 272 insertions(+), 272 deletions(-) diff --git a/src/app/(docs)/[...slug]/page.tsx b/src/app/(docs)/[...slug]/page.tsx index 7570b65e..1fe1fb07 100644 --- a/src/app/(docs)/[...slug]/page.tsx +++ b/src/app/(docs)/[...slug]/page.tsx @@ -6,9 +6,9 @@ import { DocsTitle, } from "fumadocs-ui/page"; import { notFound } from "next/navigation"; +import { LLMCopyButton, ViewOptions } from "@/components/page-actions"; import { source } from "@/lib/source"; import { getMDXComponents } from "@/mdx-components"; -import { LLMCopyButton, ViewOptions } from "@/components/page-actions"; export default async function Page(props: { params: Promise<{ slug?: string[] }>; @@ -29,14 +29,13 @@ export default async function Page(props: { {page.data.title} {page.data.description} -
- - - -
+
+ + +
}, + _req: NextRequest, + { params }: { params: Promise<{ slug?: string[] }> }, ) { - const { slug } = await params; - const page = source.getPage(slug); - if (!page) notFound(); + const { slug } = await params; + const page = source.getPage(slug); + if (!page) notFound(); - return new NextResponse(await getLLMText(page)); + return new NextResponse(await getLLMText(page)); } export function generateStaticParams() { - return source.generateParams(); + return source.generateParams(); } diff --git a/src/components/page-actions.tsx b/src/components/page-actions.tsx index 63ccf70c..51007b65 100644 --- a/src/components/page-actions.tsx +++ b/src/components/page-actions.tsx @@ -1,247 +1,248 @@ -'use client'; -import { useMemo, useState } from 'react'; +"use client"; +import { cva } from "class-variance-authority"; import { - Check, - ChevronDown, - Copy, - ExternalLinkIcon, - MessageCircleIcon, -} from 'lucide-react'; -import { cn } from '../lib/cn'; -import { useCopyButton } from 'fumadocs-ui/utils/use-copy-button'; -import { buttonVariants } from './ui/button'; + Popover, + PopoverContent, + PopoverTrigger, +} from "fumadocs-ui/components/ui/popover"; +import { useCopyButton } from "fumadocs-ui/utils/use-copy-button"; import { - Popover, - PopoverContent, - PopoverTrigger, -} from 'fumadocs-ui/components/ui/popover'; -import { cva } from 'class-variance-authority'; + Check, + ChevronDown, + Copy, + ExternalLinkIcon, + MessageCircleIcon, +} from "lucide-react"; +import { useMemo, useState } from "react"; +import { cn } from "../lib/cn"; +import { buttonVariants } from "./ui/button"; const cache = new Map(); export function LLMCopyButton({ - /** - * A URL to fetch the raw Markdown/MDX content of page - */ - markdownUrl, + /** + * A URL to fetch the raw Markdown/MDX content of page + */ + markdownUrl, }: { - markdownUrl: string; + markdownUrl: string; }) { - const [isLoading, setLoading] = useState(false); - const [checked, onClick] = useCopyButton(async () => { - const cached = cache.get(markdownUrl); - if (cached) return navigator.clipboard.writeText(cached); + const [isLoading, setLoading] = useState(false); + const [checked, onClick] = useCopyButton(async () => { + const cached = cache.get(markdownUrl); + if (cached) return navigator.clipboard.writeText(cached); - setLoading(true); + setLoading(true); - try { - await navigator.clipboard.write([ - new ClipboardItem({ - 'text/plain': fetch(markdownUrl).then(async (res) => { - const content = await res.text(); - cache.set(markdownUrl, content); + try { + await navigator.clipboard.write([ + new ClipboardItem({ + "text/plain": fetch(markdownUrl).then(async (res) => { + const content = await res.text(); + cache.set(markdownUrl, content); - return content; - }), - }), - ]); - } finally { - setLoading(false); - } - }); + return content; + }), + }), + ]); + } finally { + setLoading(false); + } + }); - return ( - - ); + return ( + + ); } const optionVariants = cva( - 'text-sm p-2 rounded-lg inline-flex items-center gap-2 hover:text-fd-accent-foreground hover:bg-fd-accent [&_svg]:size-4', + "text-sm p-2 rounded-lg inline-flex items-center gap-2 hover:text-fd-accent-foreground hover:bg-fd-accent [&_svg]:size-4", ); export function ViewOptions({ - markdownUrl, - githubUrl, + markdownUrl, + githubUrl, }: { - /** - * A URL to the raw Markdown/MDX content of page - */ - markdownUrl: string; + /** + * A URL to the raw Markdown/MDX content of page + */ + markdownUrl: string; - /** - * Source file URL on GitHub - */ - githubUrl: string; + /** + * Source file URL on GitHub + */ + githubUrl: string; }) { - const items = useMemo(() => { - const fullMarkdownUrl = - typeof window !== 'undefined' - ? new URL(markdownUrl, window.location.origin) - : 'loading'; - const q = `Read ${fullMarkdownUrl}, I want to ask questions about it.`; + const items = useMemo(() => { + const fullMarkdownUrl = + typeof window !== "undefined" + ? new URL(markdownUrl, window.location.origin) + : "loading"; + const q = `Read ${fullMarkdownUrl}, I want to ask questions about it.`; - return [ - { - title: 'Open in GitHub', - href: githubUrl, - icon: ( - - GitHub - - - ), - }, - { - title: 'Open in Scira AI', - href: `https://scira.ai/?${new URLSearchParams({ - q, - })}`, - icon: ( - - Scira AI - - - - - - - - - ), - }, - { - title: 'Open in ChatGPT', - href: `https://chatgpt.com/?${new URLSearchParams({ - hints: 'search', - q, - })}`, - icon: ( - - OpenAI - - - ), - }, - { - title: 'Open in Claude', - href: `https://claude.ai/new?${new URLSearchParams({ - q, - })}`, - icon: ( - - Anthropic - - - ), - }, - { - title: 'Open in T3 Chat', - href: `https://t3.chat/new?${new URLSearchParams({ - q, - })}`, - icon: , - }, - ]; - }, [githubUrl, markdownUrl]); + return [ + { + title: "Open in GitHub", + href: githubUrl, + icon: ( + + GitHub + + + ), + }, + { + title: "Open in Scira AI", + href: `https://scira.ai/?${new URLSearchParams({ + q, + })}`, + icon: ( + + Scira AI + + + + + + + + + ), + }, + { + title: "Open in ChatGPT", + href: `https://chatgpt.com/?${new URLSearchParams({ + hints: "search", + q, + })}`, + icon: ( + + OpenAI + + + ), + }, + { + title: "Open in Claude", + href: `https://claude.ai/new?${new URLSearchParams({ + q, + })}`, + icon: ( + + Anthropic + + + ), + }, + { + title: "Open in T3 Chat", + href: `https://t3.chat/new?${new URLSearchParams({ + q, + })}`, + icon: , + }, + ]; + }, [githubUrl, markdownUrl]); - return ( - - - Open - - - - {items.map((item) => ( - - {item.icon} - {item.title} - - - ))} - - - ); + return ( + + + Open + + + + {items.map((item) => ( + + {item.icon} + {item.title} + + + ))} + + + ); } diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx index 5fca2adb..7df62b94 100644 --- a/src/components/ui/button.tsx +++ b/src/components/ui/button.tsx @@ -1,28 +1,28 @@ -import { cva, type VariantProps } from 'class-variance-authority'; +import { cva, type VariantProps } from "class-variance-authority"; const variants = { - primary: 'bg-fd-primary text-fd-primary-foreground hover:bg-fd-primary/80', - outline: 'border hover:bg-fd-accent hover:text-fd-accent-foreground', - ghost: 'hover:bg-fd-accent hover:text-fd-accent-foreground', - secondary: - 'border bg-fd-secondary text-fd-secondary-foreground hover:bg-fd-accent hover:text-fd-accent-foreground', + primary: "bg-fd-primary text-fd-primary-foreground hover:bg-fd-primary/80", + outline: "border hover:bg-fd-accent hover:text-fd-accent-foreground", + ghost: "hover:bg-fd-accent hover:text-fd-accent-foreground", + secondary: + "border bg-fd-secondary text-fd-secondary-foreground hover:bg-fd-accent hover:text-fd-accent-foreground", } as const; export const buttonVariants = cva( - 'inline-flex items-center justify-center rounded-md p-2 text-sm font-medium transition-colors duration-100 disabled:pointer-events-none disabled:opacity-50 focus-visible:outline-none', - { - variants: { - variant: variants, - // fumadocs use `color` instead of `variant` - color: variants, - size: { - sm: 'gap-1 px-2 py-1.5 text-xs', - icon: 'p-1.5 [&_svg]:size-5', - 'icon-sm': 'p-1.5 [&_svg]:size-4.5', - 'icon-xs': 'p-1 [&_svg]:size-4', - }, - }, - }, + "inline-flex items-center justify-center rounded-md p-2 text-sm font-medium transition-colors duration-100 disabled:pointer-events-none disabled:opacity-50 focus-visible:outline-none", + { + variants: { + variant: variants, + // fumadocs use `color` instead of `variant` + color: variants, + size: { + sm: "gap-1 px-2 py-1.5 text-xs", + icon: "p-1.5 [&_svg]:size-5", + "icon-sm": "p-1.5 [&_svg]:size-4.5", + "icon-xs": "p-1 [&_svg]:size-4", + }, + }, + }, ); export type ButtonProps = VariantProps; diff --git a/src/lib/cn.ts b/src/lib/cn.ts index ba66fd25..8e473dac 100644 --- a/src/lib/cn.ts +++ b/src/lib/cn.ts @@ -1 +1 @@ -export { twMerge as cn } from 'tailwind-merge'; +export { twMerge as cn } from "tailwind-merge"; diff --git a/src/lib/get-llm-text.ts b/src/lib/get-llm-text.ts index f23ffc79..8c91bfcb 100644 --- a/src/lib/get-llm-text.ts +++ b/src/lib/get-llm-text.ts @@ -1,10 +1,10 @@ +import fs from "node:fs/promises"; +import type { InferPageType } from "fumadocs-core/source"; +import { remarkInclude } from "fumadocs-mdx/config"; import { remark } from "remark"; import remarkGfm from "remark-gfm"; import remarkMdx from "remark-mdx"; -import { remarkInclude } from "fumadocs-mdx/config"; -import { source } from "./source"; -import type { InferPageType } from "fumadocs-core/source"; -import fs from "node:fs/promises"; +import type { source } from "./source"; const processor = remark() .use(remarkMdx) diff --git a/src/lib/source.ts b/src/lib/source.ts index ee726268..46a4a1ee 100644 --- a/src/lib/source.ts +++ b/src/lib/source.ts @@ -1,8 +1,8 @@ import { loader } from "fumadocs-core/source"; +import { transformerOpenAPI } from "fumadocs-openapi/server"; import { icons } from "lucide-react"; import { createElement } from "react"; import { docs } from "@/.source"; -import { transformerOpenAPI } from "fumadocs-openapi/server"; // See https://fumadocs.vercel.app/docs/headless/source-api for more info export const source = loader({ diff --git a/src/mdx-components.tsx b/src/mdx-components.tsx index 6a17d5f1..9ece3262 100644 --- a/src/mdx-components.tsx +++ b/src/mdx-components.tsx @@ -1,3 +1,4 @@ +import { APIPage } from "fumadocs-openapi/ui"; import defaultMdxComponents from "fumadocs-ui/mdx"; import { AnchorIcon, @@ -17,9 +18,8 @@ import { } from "lucide-react"; import type { MDXComponents } from "mdx/types"; import { Mermaid } from "@/components/mdx/mermaid"; -import OZWizard from "./components/oz-wizard"; -import { APIPage } from "fumadocs-openapi/ui"; import { openapi } from "@/lib/openapi"; +import OZWizard from "./components/oz-wizard"; // use this function to get MDX components, you will need it for rendering MDX export function getMDXComponents(components?: MDXComponents): MDXComponents { From e1cb638dd0317d210e668dee9f042d89f3ac2b1f Mon Sep 17 00:00:00 2001 From: Steve Date: Tue, 9 Sep 2025 11:26:34 -0400 Subject: [PATCH 09/14] chore: updated nav --- package.json | 16 +- pnpm-lock.yaml | 457 +++++++--------- src/app/(docs)/[...slug]/layout.tsx | 5 +- src/app/layout.config.tsx | 2 +- src/components/language-toggle.tsx | 68 +++ src/components/layout/docs/client.tsx | 136 +++++ src/components/layout/docs/index.tsx | 574 +++++++++++++++++++++ src/components/layout/docs/page-client.tsx | 417 +++++++++++++++ src/components/layout/docs/page.tsx | 103 ++++ src/components/layout/page.tsx | 288 +++++++++++ src/components/layout/shared/client.tsx | 30 ++ src/components/layout/shared/index.tsx | 182 +++++++ src/components/root-toggle.tsx | 103 ++++ src/components/search-toggle.tsx | 79 +++ src/components/sidebar.tsx | 559 ++++++++++++++++++++ src/components/theme-toggle.tsx | 87 ++++ src/components/ui/button.tsx | 40 +- src/components/ui/collapsible.tsx | 39 ++ src/components/ui/popover.tsx | 32 ++ src/components/ui/scroll-area.tsx | 58 +++ src/components/ui/toc-clerk.tsx | 179 +++++++ src/components/ui/toc-thumb.tsx | 73 +++ src/components/ui/toc.tsx | 101 ++++ src/lib/cn.ts | 2 +- src/lib/is-active.ts | 23 + src/lib/merge-refs.ts | 15 + 26 files changed, 3371 insertions(+), 297 deletions(-) create mode 100644 src/components/language-toggle.tsx create mode 100644 src/components/layout/docs/client.tsx create mode 100644 src/components/layout/docs/index.tsx create mode 100644 src/components/layout/docs/page-client.tsx create mode 100644 src/components/layout/docs/page.tsx create mode 100644 src/components/layout/page.tsx create mode 100644 src/components/layout/shared/client.tsx create mode 100644 src/components/layout/shared/index.tsx create mode 100644 src/components/root-toggle.tsx create mode 100644 src/components/search-toggle.tsx create mode 100644 src/components/sidebar.tsx create mode 100644 src/components/theme-toggle.tsx create mode 100644 src/components/ui/collapsible.tsx create mode 100644 src/components/ui/popover.tsx create mode 100644 src/components/ui/scroll-area.tsx create mode 100644 src/components/ui/toc-clerk.tsx create mode 100644 src/components/ui/toc-thumb.tsx create mode 100644 src/components/ui/toc.tsx create mode 100644 src/lib/is-active.ts create mode 100644 src/lib/merge-refs.ts diff --git a/package.json b/package.json index a1326561..50de0aec 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,11 @@ }, "dependencies": { "@fumadocs/mdx-remote": "^1.4.0", - "@netlify/plugin-nextjs": "^5.13.0", + "@netlify/plugin-nextjs": "^5.13.1", + "@radix-ui/react-collapsible": "^1.1.12", + "@radix-ui/react-popover": "^1.1.15", + "@radix-ui/react-presence": "^1.1.5", + "@radix-ui/react-scroll-area": "^1.2.10", "@radix-ui/react-separator": "^1.1.7", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", @@ -27,7 +31,7 @@ "glob": "^11.0.3", "katex": "^0.16.22", "lucide-react": "^0.540.0", - "mermaid": "^11.10.1", + "mermaid": "^11.11.0", "next": "15.4.5", "next-themes": "^0.4.6", "react": "^19.1.1", @@ -41,15 +45,15 @@ "tailwind-merge": "^3.3.1" }, "devDependencies": { - "@biomejs/biome": "^2.2.2", - "@tailwindcss/postcss": "^4.1.12", + "@biomejs/biome": "^2.2.3", + "@tailwindcss/postcss": "^4.1.13", "@types/mdx": "^2.0.13", "@types/node": "24.1.0", "@types/react": "^19.1.12", "@types/react-dom": "^19.1.9", "postcss": "^8.5.6", - "tailwindcss": "^4.1.12", - "tw-animate-css": "^1.3.7", + "tailwindcss": "^4.1.13", + "tw-animate-css": "^1.3.8", "typescript": "^5.9.2" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 53ff68b9..6520ca4a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,8 +12,20 @@ importers: specifier: ^1.4.0 version: 1.4.0(@types/react@19.1.12)(fumadocs-core@15.7.7(@types/react@19.1.12)(next@15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1) '@netlify/plugin-nextjs': - specifier: ^5.13.0 - version: 5.13.0 + specifier: ^5.13.1 + version: 5.13.1 + '@radix-ui/react-collapsible': + specifier: ^1.1.12 + version: 1.1.12(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-popover': + specifier: ^1.1.15 + version: 1.1.15(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-presence': + specifier: ^1.1.5 + version: 1.1.5(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-scroll-area': + specifier: ^1.2.10 + version: 1.2.10(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@radix-ui/react-separator': specifier: ^1.1.7 version: 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) @@ -31,10 +43,10 @@ importers: version: 11.8.2(@fumadocs/mdx-remote@1.4.0(@types/react@19.1.12)(fumadocs-core@15.7.7(@types/react@19.1.12)(next@15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1))(fumadocs-core@15.7.7(@types/react@19.1.12)(next@15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(next@15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1) fumadocs-openapi: specifier: ^9.3.7 - version: 9.3.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(next@15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(tailwindcss@4.1.12) + version: 9.3.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(next@15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(tailwindcss@4.1.13) fumadocs-ui: specifier: 15.7.7 - version: 15.7.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(next@15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(tailwindcss@4.1.12) + version: 15.7.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(next@15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(tailwindcss@4.1.13) glob: specifier: ^11.0.3 version: 11.0.3 @@ -45,8 +57,8 @@ importers: specifier: ^0.540.0 version: 0.540.0(react@19.1.1) mermaid: - specifier: ^11.10.1 - version: 11.10.1 + specifier: ^11.11.0 + version: 11.11.0 next: specifier: 15.4.5 version: 15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1) @@ -82,11 +94,11 @@ importers: version: 3.3.1 devDependencies: '@biomejs/biome': - specifier: ^2.2.2 - version: 2.2.2 + specifier: ^2.2.3 + version: 2.2.3 '@tailwindcss/postcss': - specifier: ^4.1.12 - version: 4.1.12 + specifier: ^4.1.13 + version: 4.1.13 '@types/mdx': specifier: ^2.0.13 version: 2.0.13 @@ -103,11 +115,11 @@ importers: specifier: ^8.5.6 version: 8.5.6 tailwindcss: - specifier: ^4.1.12 - version: 4.1.12 + specifier: ^4.1.13 + version: 4.1.13 tw-animate-css: - specifier: ^1.3.7 - version: 1.3.7 + specifier: ^1.3.8 + version: 1.3.8 typescript: specifier: ^5.9.2 version: 5.9.2 @@ -121,62 +133,62 @@ packages: '@antfu/install-pkg@1.1.0': resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==} - '@antfu/utils@8.1.1': - resolution: {integrity: sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ==} + '@antfu/utils@9.2.0': + resolution: {integrity: sha512-Oq1d9BGZakE/FyoEtcNeSwM7MpDO2vUBi11RWBZXf75zPsbUVWmUs03EqkRFrcgbXyKTas0BdZWC1wcuSoqSAw==} '@apidevtools/json-schema-ref-parser@11.9.3': resolution: {integrity: sha512-60vepv88RwcJtSHrD6MjIL6Ta3SOYbgfnkHb+ppAVK+o9mXprRtulx7VlRl3lN3bbvysAfCS7WMVfhUYemB0IQ==} engines: {node: '>= 16'} - '@biomejs/biome@2.2.2': - resolution: {integrity: sha512-j1omAiQWCkhuLgwpMKisNKnsM6W8Xtt1l0WZmqY/dFj8QPNkIoTvk4tSsi40FaAAkBE1PU0AFG2RWFBWenAn+w==} + '@biomejs/biome@2.2.3': + resolution: {integrity: sha512-9w0uMTvPrIdvUrxazZ42Ib7t8Y2yoGLKLdNne93RLICmaHw7mcLv4PPb5LvZLJF3141gQHiCColOh/v6VWlWmg==} engines: {node: '>=14.21.3'} hasBin: true - '@biomejs/cli-darwin-arm64@2.2.2': - resolution: {integrity: sha512-6ePfbCeCPryWu0CXlzsWNZgVz/kBEvHiPyNpmViSt6A2eoDf4kXs3YnwQPzGjy8oBgQulrHcLnJL0nkCh80mlQ==} + '@biomejs/cli-darwin-arm64@2.2.3': + resolution: {integrity: sha512-OrqQVBpadB5eqzinXN4+Q6honBz+tTlKVCsbEuEpljK8ASSItzIRZUA02mTikl3H/1nO2BMPFiJ0nkEZNy3B1w==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [darwin] - '@biomejs/cli-darwin-x64@2.2.2': - resolution: {integrity: sha512-Tn4JmVO+rXsbRslml7FvKaNrlgUeJot++FkvYIhl1OkslVCofAtS35MPlBMhXgKWF9RNr9cwHanrPTUUXcYGag==} + '@biomejs/cli-darwin-x64@2.2.3': + resolution: {integrity: sha512-OCdBpb1TmyfsTgBAM1kPMXyYKTohQ48WpiN9tkt9xvU6gKVKHY4oVwteBebiOqyfyzCNaSiuKIPjmHjUZ2ZNMg==} engines: {node: '>=14.21.3'} cpu: [x64] os: [darwin] - '@biomejs/cli-linux-arm64-musl@2.2.2': - resolution: {integrity: sha512-/MhYg+Bd6renn6i1ylGFL5snYUn/Ct7zoGVKhxnro3bwekiZYE8Kl39BSb0MeuqM+72sThkQv4TnNubU9njQRw==} + '@biomejs/cli-linux-arm64-musl@2.2.3': + resolution: {integrity: sha512-q3w9jJ6JFPZPeqyvwwPeaiS/6NEszZ+pXKF+IczNo8Xj6fsii45a4gEEicKyKIytalV+s829ACZujQlXAiVLBQ==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] - '@biomejs/cli-linux-arm64@2.2.2': - resolution: {integrity: sha512-JfrK3gdmWWTh2J5tq/rcWCOsImVyzUnOS2fkjhiYKCQ+v8PqM+du5cfB7G1kXas+7KQeKSWALv18iQqdtIMvzw==} + '@biomejs/cli-linux-arm64@2.2.3': + resolution: {integrity: sha512-g/Uta2DqYpECxG+vUmTAmUKlVhnGEcY7DXWgKP8ruLRa8Si1QHsWknPY3B/wCo0KgYiFIOAZ9hjsHfNb9L85+g==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] - '@biomejs/cli-linux-x64-musl@2.2.2': - resolution: {integrity: sha512-ZCLXcZvjZKSiRY/cFANKg+z6Fhsf9MHOzj+NrDQcM+LbqYRT97LyCLWy2AS+W2vP+i89RyRM+kbGpUzbRTYWig==} + '@biomejs/cli-linux-x64-musl@2.2.3': + resolution: {integrity: sha512-y76Dn4vkP1sMRGPFlNc+OTETBhGPJ90jY3il6jAfur8XWrYBQV3swZ1Jo0R2g+JpOeeoA0cOwM7mJG6svDz79w==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] - '@biomejs/cli-linux-x64@2.2.2': - resolution: {integrity: sha512-Ogb+77edO5LEP/xbNicACOWVLt8mgC+E1wmpUakr+O4nKwLt9vXe74YNuT3T1dUBxC/SnrVmlzZFC7kQJEfquQ==} + '@biomejs/cli-linux-x64@2.2.3': + resolution: {integrity: sha512-LEtyYL1fJsvw35CxrbQ0gZoxOG3oZsAjzfRdvRBRHxOpQ91Q5doRVjvWW/wepgSdgk5hlaNzfeqpyGmfSD0Eyw==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] - '@biomejs/cli-win32-arm64@2.2.2': - resolution: {integrity: sha512-wBe2wItayw1zvtXysmHJQoQqXlTzHSpQRyPpJKiNIR21HzH/CrZRDFic1C1jDdp+zAPtqhNExa0owKMbNwW9cQ==} + '@biomejs/cli-win32-arm64@2.2.3': + resolution: {integrity: sha512-Ms9zFYzjcJK7LV+AOMYnjN3pV3xL8Prxf9aWdDVL74onLn5kcvZ1ZMQswE5XHtnd/r/0bnUd928Rpbs14BzVmA==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [win32] - '@biomejs/cli-win32-x64@2.2.2': - resolution: {integrity: sha512-DAuHhHekGfiGb6lCcsT4UyxQmVwQiBCBUMwVra/dcOSs9q8OhfaZgey51MlekT3p8UwRqtXQfFuEJBhJNdLZwg==} + '@biomejs/cli-win32-x64@2.2.3': + resolution: {integrity: sha512-gvCpewE7mBwBIpqk1YrUqNR4mCiyJm6UI3YWQQXkedSSEwzRdodRpaKhbdbHw1/hmTWOVXQ+Eih5Qctf4TCVOQ==} engines: {node: '>=14.21.3'} cpu: [x64] os: [win32] @@ -393,8 +405,8 @@ packages: '@iconify/types@2.0.0': resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} - '@iconify/utils@2.3.0': - resolution: {integrity: sha512-GmQ78prtwYW6EtzXRU1rY+KwOKfz32PD7iJh6Iyqw68GiKuoZ2A6pRtzWONz5VQJbp50mEjXh/7NkumtrAgRKA==} + '@iconify/utils@3.0.1': + resolution: {integrity: sha512-A78CUEnFGX8I/WlILxJCuIJXloL0j/OJ9PSchPAfCargEIKmUBWvvEMmKWB5oONwiUqlNt+5eRufdkLxeHIWYw==} '@img/sharp-darwin-arm64@0.34.3': resolution: {integrity: sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==} @@ -559,8 +571,8 @@ packages: '@mermaid-js/parser@0.6.2': resolution: {integrity: sha512-+PO02uGF6L6Cs0Bw8RpGhikVvMWEysfAyl27qTlroUB8jSWr1lL0Sf6zi78ZxlSnmgSY2AMMKVgghnN9jTtwkQ==} - '@netlify/plugin-nextjs@5.13.0': - resolution: {integrity: sha512-sOhMUITRmcQDFf+ppAhZwecCE2kDCV8nVWe3fwTkNThpKoXBcpKVECL07IkBv5fJLxp/kODy346OaCk6FI9MAg==} + '@netlify/plugin-nextjs@5.13.1': + resolution: {integrity: sha512-dAswWBpvjtkqTd6AmgPfwQI7kNnCEnaz0SjlWYtN/H0FTY6fkpo+0yq8fVKWzKu+5ckO0QzNPMoWfJ4chDXcrw==} engines: {node: '>=18.0.0'} '@next/env@15.4.5': @@ -614,10 +626,6 @@ packages: cpu: [x64] os: [win32] - '@orama/orama@3.1.12': - resolution: {integrity: sha512-U7PY8FwXHuJ6bNBpbsqe0KLzb91IcJuORDggqHHkFy1waokY5SpWLN9tzB3AOW776awp6s1bjwts9I9Davy3lw==} - engines: {node: '>= 20.0.0'} - '@orama/orama@3.1.13': resolution: {integrity: sha512-O0hdKt4K31i8fpq8Bw5RfdPVAqm0EdduBUcluPo2MRcfCOwUEf5JlnvRhf/J0ezOYOD8jQ/LumYZxOVi/XK/BA==} engines: {node: '>= 20.0.0'} @@ -1012,51 +1020,27 @@ packages: resolution: {integrity: sha512-QHSvHBVDze3+dUwAhIGq6l1iOev4jdoqdBK7QpfeN1Q4h+6qpVEw3EEqBiH0AXUSh/iWwObBv4uMgfIx0aNZ5g==} engines: {node: '>=20'} - '@shikijs/core@3.12.1': - resolution: {integrity: sha512-j9+UDQ6M50xvaSR/e9lg212H0Fqxy3lYd39Q6YITYQxfrb5VYNUKPLZp4PN9f+YmRcdpyNAm3obn/tIZ2WkUWg==} - '@shikijs/core@3.12.2': resolution: {integrity: sha512-L1Safnhra3tX/oJK5kYHaWmLEBJi1irASwewzY3taX5ibyXyMkkSDZlq01qigjryOBwrXSdFgTiZ3ryzSNeu7Q==} - '@shikijs/engine-javascript@3.12.1': - resolution: {integrity: sha512-mwif5T3rEBSMn/1m9dNi4WmB4dxH4VfYqreQMLpbFYov8MM3Gus98I549amFMjtEmYDAkTKGP7bmsv1n9t9I+A==} - '@shikijs/engine-javascript@3.12.2': resolution: {integrity: sha512-Nm3/azSsaVS7hk6EwtHEnTythjQfwvrO5tKqMlaH9TwG1P+PNaR8M0EAKZ+GaH2DFwvcr4iSfTveyxMIvXEHMw==} - '@shikijs/engine-oniguruma@3.12.1': - resolution: {integrity: sha512-hbYq+XOc55CU7Irkhsgwh8WgQbx2W5IVzHV4l+wZ874olMLSNg5o3F73vo9m4SAhimFyqq/86xnx9h+T30HhhQ==} - '@shikijs/engine-oniguruma@3.12.2': resolution: {integrity: sha512-hozwnFHsLvujK4/CPVHNo3Bcg2EsnG8krI/ZQ2FlBlCRpPZW4XAEQmEwqegJsypsTAN9ehu2tEYe30lYKSZW/w==} - '@shikijs/langs@3.12.1': - resolution: {integrity: sha512-Y1MbMfVO5baRz7Boo7EoD36TmzfUx/I5n8e+wZumx6SlUA81Zj1ZwNJL871iIuSHrdsheV4AxJtHQ9mlooklmg==} - '@shikijs/langs@3.12.2': resolution: {integrity: sha512-bVx5PfuZHDSHoBal+KzJZGheFuyH4qwwcwG/n+MsWno5cTlKmaNtTsGzJpHYQ8YPbB5BdEdKU1rga5/6JGY8ww==} - '@shikijs/rehype@3.12.1': - resolution: {integrity: sha512-MEWt7qNyvlzVsQAf6WWTX+EN5IxRW3VUfzxrmQQHOUXjGePjQcVpHuaapW/BxgUX5hNhXKUtBVR7KMwY3ASLjA==} - '@shikijs/rehype@3.12.2': resolution: {integrity: sha512-9wg+FKv0ByaQScTonpZdrDhADOoJP/yCWLAuiYYG6GehwNV5rGwnLvWKj33UmtLedKMSHzWUdB+Un6rfDFo/FA==} - '@shikijs/themes@3.12.1': - resolution: {integrity: sha512-9JrAm9cA5hqM/YXymA3oAAZdnCgQf1zyrNDtsnM105nNEoEpux4dyzdoOjc2KawEKj1iUs/WH2ota6Atp7GYkQ==} - '@shikijs/themes@3.12.2': resolution: {integrity: sha512-fTR3QAgnwYpfGczpIbzPjlRnxyONJOerguQv1iwpyQZ9QXX4qy/XFQqXlf17XTsorxnHoJGbH/LXBvwtqDsF5A==} - '@shikijs/transformers@3.12.1': - resolution: {integrity: sha512-crGh3cSZf6mwg3K2W8i79Ja+q4tVClRHdHLnUGi5arS58+cqdzsbkrEZBDMyevf9ehmjFUWDTEwCMEyp9I3z0g==} - '@shikijs/transformers@3.12.2': resolution: {integrity: sha512-+z1aMq4N5RoNGY8i7qnTYmG2MBYzFmwkm/yOd6cjEI7OVzcldVvzQCfxU1YbIVgsyB0xHVc2jFe1JhgoXyUoSQ==} - '@shikijs/types@3.12.1': - resolution: {integrity: sha512-Is/p+1vTss22LIsGCJTmGrxu7ZC1iBL9doJFYLaZ4aI8d0VDXb7Mn0kBzhkc7pdsRpmUbQLQ5HXwNpa3H6F8og==} - '@shikijs/types@3.12.2': resolution: {integrity: sha512-K5UIBzxCyv0YoxN3LMrKB9zuhp1bV+LgewxuVwHdl4Gz5oePoUFrr9EfgJlGlDeXCU1b/yhdnXeuRvAnz8HN8Q==} @@ -1069,65 +1053,65 @@ packages: '@swc/helpers@0.5.15': resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} - '@tailwindcss/node@4.1.12': - resolution: {integrity: sha512-3hm9brwvQkZFe++SBt+oLjo4OLDtkvlE8q2WalaD/7QWaeM7KEJbAiY/LJZUaCs7Xa8aUu4xy3uoyX4q54UVdQ==} + '@tailwindcss/node@4.1.13': + resolution: {integrity: sha512-eq3ouolC1oEFOAvOMOBAmfCIqZBJuvWvvYWh5h5iOYfe1HFC6+GZ6EIL0JdM3/niGRJmnrOc+8gl9/HGUaaptw==} - '@tailwindcss/oxide-android-arm64@4.1.12': - resolution: {integrity: sha512-oNY5pq+1gc4T6QVTsZKwZaGpBb2N1H1fsc1GD4o7yinFySqIuRZ2E4NvGasWc6PhYJwGK2+5YT1f9Tp80zUQZQ==} + '@tailwindcss/oxide-android-arm64@4.1.13': + resolution: {integrity: sha512-BrpTrVYyejbgGo57yc8ieE+D6VT9GOgnNdmh5Sac6+t0m+v+sKQevpFVpwX3pBrM2qKrQwJ0c5eDbtjouY/+ew==} engines: {node: '>= 10'} cpu: [arm64] os: [android] - '@tailwindcss/oxide-darwin-arm64@4.1.12': - resolution: {integrity: sha512-cq1qmq2HEtDV9HvZlTtrj671mCdGB93bVY6J29mwCyaMYCP/JaUBXxrQQQm7Qn33AXXASPUb2HFZlWiiHWFytw==} + '@tailwindcss/oxide-darwin-arm64@4.1.13': + resolution: {integrity: sha512-YP+Jksc4U0KHcu76UhRDHq9bx4qtBftp9ShK/7UGfq0wpaP96YVnnjFnj3ZFrUAjc5iECzODl/Ts0AN7ZPOANQ==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@tailwindcss/oxide-darwin-x64@4.1.12': - resolution: {integrity: sha512-6UCsIeFUcBfpangqlXay9Ffty9XhFH1QuUFn0WV83W8lGdX8cD5/+2ONLluALJD5+yJ7k8mVtwy3zMZmzEfbLg==} + '@tailwindcss/oxide-darwin-x64@4.1.13': + resolution: {integrity: sha512-aAJ3bbwrn/PQHDxCto9sxwQfT30PzyYJFG0u/BWZGeVXi5Hx6uuUOQEI2Fa43qvmUjTRQNZnGqe9t0Zntexeuw==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@tailwindcss/oxide-freebsd-x64@4.1.12': - resolution: {integrity: sha512-JOH/f7j6+nYXIrHobRYCtoArJdMJh5zy5lr0FV0Qu47MID/vqJAY3r/OElPzx1C/wdT1uS7cPq+xdYYelny1ww==} + '@tailwindcss/oxide-freebsd-x64@4.1.13': + resolution: {integrity: sha512-Wt8KvASHwSXhKE/dJLCCWcTSVmBj3xhVhp/aF3RpAhGeZ3sVo7+NTfgiN8Vey/Fi8prRClDs6/f0KXPDTZE6nQ==} engines: {node: '>= 10'} cpu: [x64] os: [freebsd] - '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.12': - resolution: {integrity: sha512-v4Ghvi9AU1SYgGr3/j38PD8PEe6bRfTnNSUE3YCMIRrrNigCFtHZ2TCm8142X8fcSqHBZBceDx+JlFJEfNg5zQ==} + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.13': + resolution: {integrity: sha512-mbVbcAsW3Gkm2MGwA93eLtWrwajz91aXZCNSkGTx/R5eb6KpKD5q8Ueckkh9YNboU8RH7jiv+ol/I7ZyQ9H7Bw==} engines: {node: '>= 10'} cpu: [arm] os: [linux] - '@tailwindcss/oxide-linux-arm64-gnu@4.1.12': - resolution: {integrity: sha512-YP5s1LmetL9UsvVAKusHSyPlzSRqYyRB0f+Kl/xcYQSPLEw/BvGfxzbH+ihUciePDjiXwHh+p+qbSP3SlJw+6g==} + '@tailwindcss/oxide-linux-arm64-gnu@4.1.13': + resolution: {integrity: sha512-wdtfkmpXiwej/yoAkrCP2DNzRXCALq9NVLgLELgLim1QpSfhQM5+ZxQQF8fkOiEpuNoKLp4nKZ6RC4kmeFH0HQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@tailwindcss/oxide-linux-arm64-musl@4.1.12': - resolution: {integrity: sha512-V8pAM3s8gsrXcCv6kCHSuwyb/gPsd863iT+v1PGXC4fSL/OJqsKhfK//v8P+w9ThKIoqNbEnsZqNy+WDnwQqCA==} + '@tailwindcss/oxide-linux-arm64-musl@4.1.13': + resolution: {integrity: sha512-hZQrmtLdhyqzXHB7mkXfq0IYbxegaqTmfa1p9MBj72WPoDD3oNOh1Lnxf6xZLY9C3OV6qiCYkO1i/LrzEdW2mg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@tailwindcss/oxide-linux-x64-gnu@4.1.12': - resolution: {integrity: sha512-xYfqYLjvm2UQ3TZggTGrwxjYaLB62b1Wiysw/YE3Yqbh86sOMoTn0feF98PonP7LtjsWOWcXEbGqDL7zv0uW8Q==} + '@tailwindcss/oxide-linux-x64-gnu@4.1.13': + resolution: {integrity: sha512-uaZTYWxSXyMWDJZNY1Ul7XkJTCBRFZ5Fo6wtjrgBKzZLoJNrG+WderJwAjPzuNZOnmdrVg260DKwXCFtJ/hWRQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@tailwindcss/oxide-linux-x64-musl@4.1.12': - resolution: {integrity: sha512-ha0pHPamN+fWZY7GCzz5rKunlv9L5R8kdh+YNvP5awe3LtuXb5nRi/H27GeL2U+TdhDOptU7T6Is7mdwh5Ar3A==} + '@tailwindcss/oxide-linux-x64-musl@4.1.13': + resolution: {integrity: sha512-oXiPj5mi4Hdn50v5RdnuuIms0PVPI/EG4fxAfFiIKQh5TgQgX7oSuDWntHW7WNIi/yVLAiS+CRGW4RkoGSSgVQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@tailwindcss/oxide-wasm32-wasi@4.1.12': - resolution: {integrity: sha512-4tSyu3dW+ktzdEpuk6g49KdEangu3eCYoqPhWNsZgUhyegEda3M9rG0/j1GV/JjVVsj+lG7jWAyrTlLzd/WEBg==} + '@tailwindcss/oxide-wasm32-wasi@4.1.13': + resolution: {integrity: sha512-+LC2nNtPovtrDwBc/nqnIKYh/W2+R69FA0hgoeOn64BdCX522u19ryLh3Vf3F8W49XBcMIxSe665kwy21FkhvA==} engines: {node: '>=14.0.0'} cpu: [wasm32] bundledDependencies: @@ -1138,24 +1122,24 @@ packages: - '@emnapi/wasi-threads' - tslib - '@tailwindcss/oxide-win32-arm64-msvc@4.1.12': - resolution: {integrity: sha512-iGLyD/cVP724+FGtMWslhcFyg4xyYyM+5F4hGvKA7eifPkXHRAUDFaimu53fpNg9X8dfP75pXx/zFt/jlNF+lg==} + '@tailwindcss/oxide-win32-arm64-msvc@4.1.13': + resolution: {integrity: sha512-dziTNeQXtoQ2KBXmrjCxsuPk3F3CQ/yb7ZNZNA+UkNTeiTGgfeh+gH5Pi7mRncVgcPD2xgHvkFCh/MhZWSgyQg==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@tailwindcss/oxide-win32-x64-msvc@4.1.12': - resolution: {integrity: sha512-NKIh5rzw6CpEodv/++r0hGLlfgT/gFN+5WNdZtvh6wpU2BpGNgdjvj6H2oFc8nCM839QM1YOhjpgbAONUb4IxA==} + '@tailwindcss/oxide-win32-x64-msvc@4.1.13': + resolution: {integrity: sha512-3+LKesjXydTkHk5zXX01b5KMzLV1xl2mcktBJkje7rhFUpUlYJy7IMOLqjIRQncLTa1WZZiFY/foAeB5nmaiTw==} engines: {node: '>= 10'} cpu: [x64] os: [win32] - '@tailwindcss/oxide@4.1.12': - resolution: {integrity: sha512-gM5EoKHW/ukmlEtphNwaGx45fGoEmP10v51t9unv55voWh6WrOL19hfuIdo2FjxIaZzw776/BUQg7Pck++cIVw==} + '@tailwindcss/oxide@4.1.13': + resolution: {integrity: sha512-CPgsM1IpGRa880sMbYmG1s4xhAy3xEt1QULgTJGQmZUeNgXFR7s1YxYygmJyBGtou4SyEosGAGEeYqY7R53bIA==} engines: {node: '>= 10'} - '@tailwindcss/postcss@4.1.12': - resolution: {integrity: sha512-5PpLYhCAwf9SJEeIsSmCDLgyVfdBhdBpzX1OJ87anT9IVR0Z9pjM0FNixCAUAHGnMBGB8K99SwAheXrT0Kh6QQ==} + '@tailwindcss/postcss@4.1.13': + resolution: {integrity: sha512-HLgx6YSFKJT7rJqh9oJs/TkBFhxuMOfUKSBEPYwV+t78POOBsdQ7crhZLzwcH3T0UyUuOzU/GK5pk5eKr3wCiQ==} '@types/d3-array@3.2.1': resolution: {integrity: sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==} @@ -1336,16 +1320,16 @@ packages: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - ansi-regex@6.2.0: - resolution: {integrity: sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==} + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} engines: {node: '>=12'} ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - ansi-styles@6.2.1: - resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} engines: {node: '>=12'} argparse@1.0.10: @@ -1365,8 +1349,8 @@ packages: bail@2.0.2: resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} - caniuse-lite@1.0.30001739: - resolution: {integrity: sha512-y+j60d6ulelrNSwpPyrHdl+9mJnQzHBr08xm48Qno0nSk4h3Qojh+ziv2qE6rXf4k3tadF4o1J/1tAbVm1NtnA==} + caniuse-lite@1.0.30001741: + resolution: {integrity: sha512-QGUGitqsc8ARjLdgAfxETDhRbJ0REsP6O3I96TAth/mVjh2cYzN2u+3AzPP3aVSm2FehEItaJw1xd+IGBXWeSw==} ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} @@ -2128,8 +2112,8 @@ packages: longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} - lru-cache@11.1.0: - resolution: {integrity: sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==} + lru-cache@11.2.1: + resolution: {integrity: sha512-r8LA6i4LP4EeWOhqBaZZjDWwehd1xUJPCJd9Sv300H0ZmcUER4+JPh7bqqZeqs1o5pgtgvXm+d9UGrB5zZGDiQ==} engines: {node: 20 || >=22} lucide-react@0.540.0: @@ -2137,8 +2121,8 @@ packages: peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 - magic-string@0.30.18: - resolution: {integrity: sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==} + magic-string@0.30.19: + resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==} markdown-extensions@2.0.0: resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==} @@ -2147,9 +2131,9 @@ packages: markdown-table@3.0.4: resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} - marked@16.2.1: - resolution: {integrity: sha512-r3UrXED9lMlHF97jJByry90cwrZBBvZmjG1L68oYfuPMW+uDTnuMbyJDymCWwbTE+f+3LhpNDKfpR3a3saFyjA==} - engines: {node: '>= 20'} + marked@15.0.12: + resolution: {integrity: sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==} + engines: {node: '>= 18'} hasBin: true mdast-util-find-and-replace@3.0.2: @@ -2203,8 +2187,8 @@ packages: mdast-util-to-string@4.0.0: resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} - mermaid@11.10.1: - resolution: {integrity: sha512-0PdeADVWURz7VMAX0+MiMcgfxFKY4aweSGsjgFihe3XlMKNqmai/cugMrqTd3WNHM93V+K+AZL6Wu6tB5HmxRw==} + mermaid@11.11.0: + resolution: {integrity: sha512-9lb/VNkZqWTRjVgCV+l1N+t4kyi94y+l5xrmBmbbxZYkfRl5hEDaTPMOcaWKCl1McG8nBEaMlWwkcAEEgjhBgg==} micromark-core-commonmark@2.0.3: resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} @@ -2605,9 +2589,6 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - shiki@3.12.1: - resolution: {integrity: sha512-eMlxVaXyuNQAQCaMtDKQjKv0eVm+kA6fsZtv9UqKgspP+7lWCVi7SoN+cJq1dawvIDQY7TI3SixamztotM6R6Q==} - shiki@3.12.2: resolution: {integrity: sha512-uIrKI+f9IPz1zDT+GMz+0RjzKJiijVr6WDWm9Pe3NNY6QigKCfifCEv9v9R2mDASKKjzjQ2QpFLcxaR3iHSnMA==} @@ -2647,8 +2628,8 @@ packages: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} - strip-ansi@7.1.0: - resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + strip-ansi@7.1.2: + resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} engines: {node: '>=12'} strip-bom-string@1.0.0: @@ -2683,8 +2664,8 @@ packages: tailwind-merge@3.3.1: resolution: {integrity: sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==} - tailwindcss@4.1.12: - resolution: {integrity: sha512-DzFtxOi+7NsFf7DBtI3BJsynR+0Yp6etH+nRPTbpWnS2pZBaSksv/JGctNwSWzbFjp0vxSqknaUylseZqMDGrA==} + tailwindcss@4.1.13: + resolution: {integrity: sha512-i+zidfmTqtwquj4hMEwdjshYYgMbOrPzb9a0M3ZgNa0JMoZeFC6bxZvO8yr8ozS6ix2SDz0+mvryPeBs2TFE+w==} tapable@2.2.3: resolution: {integrity: sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==} @@ -2697,8 +2678,8 @@ packages: tinyexec@1.0.1: resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==} - tinyglobby@0.2.14: - resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} trim-lines@3.0.1: @@ -2714,8 +2695,8 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - tw-animate-css@1.3.7: - resolution: {integrity: sha512-lvLb3hTIpB5oGsk8JmLoAjeCHV58nKa2zHYn8yWOoG5JJusH3bhJlF2DLAZ/5NmJ+jyH3ssiAx/2KmbhavJy/A==} + tw-animate-css@1.3.8: + resolution: {integrity: sha512-Qrk3PZ7l7wUcGYhwZloqfkWCmaXZAoqjkdbIDvzfGshwGtexa/DAs9koXxIkrpEasyevandomzCBAV1Yyop5rw==} typescript@5.9.2: resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==} @@ -2858,7 +2839,7 @@ snapshots: package-manager-detector: 1.3.0 tinyexec: 1.0.1 - '@antfu/utils@8.1.1': {} + '@antfu/utils@9.2.0': {} '@apidevtools/json-schema-ref-parser@11.9.3': dependencies: @@ -2866,39 +2847,39 @@ snapshots: '@types/json-schema': 7.0.15 js-yaml: 4.1.0 - '@biomejs/biome@2.2.2': + '@biomejs/biome@2.2.3': optionalDependencies: - '@biomejs/cli-darwin-arm64': 2.2.2 - '@biomejs/cli-darwin-x64': 2.2.2 - '@biomejs/cli-linux-arm64': 2.2.2 - '@biomejs/cli-linux-arm64-musl': 2.2.2 - '@biomejs/cli-linux-x64': 2.2.2 - '@biomejs/cli-linux-x64-musl': 2.2.2 - '@biomejs/cli-win32-arm64': 2.2.2 - '@biomejs/cli-win32-x64': 2.2.2 - - '@biomejs/cli-darwin-arm64@2.2.2': + '@biomejs/cli-darwin-arm64': 2.2.3 + '@biomejs/cli-darwin-x64': 2.2.3 + '@biomejs/cli-linux-arm64': 2.2.3 + '@biomejs/cli-linux-arm64-musl': 2.2.3 + '@biomejs/cli-linux-x64': 2.2.3 + '@biomejs/cli-linux-x64-musl': 2.2.3 + '@biomejs/cli-win32-arm64': 2.2.3 + '@biomejs/cli-win32-x64': 2.2.3 + + '@biomejs/cli-darwin-arm64@2.2.3': optional: true - '@biomejs/cli-darwin-x64@2.2.2': + '@biomejs/cli-darwin-x64@2.2.3': optional: true - '@biomejs/cli-linux-arm64-musl@2.2.2': + '@biomejs/cli-linux-arm64-musl@2.2.3': optional: true - '@biomejs/cli-linux-arm64@2.2.2': + '@biomejs/cli-linux-arm64@2.2.3': optional: true - '@biomejs/cli-linux-x64-musl@2.2.2': + '@biomejs/cli-linux-x64-musl@2.2.3': optional: true - '@biomejs/cli-linux-x64@2.2.2': + '@biomejs/cli-linux-x64@2.2.3': optional: true - '@biomejs/cli-win32-arm64@2.2.2': + '@biomejs/cli-win32-arm64@2.2.3': optional: true - '@biomejs/cli-win32-x64@2.2.2': + '@biomejs/cli-win32-x64@2.2.3': optional: true '@braintree/sanitize-url@7.1.1': {} @@ -3044,10 +3025,10 @@ snapshots: '@iconify/types@2.0.0': {} - '@iconify/utils@2.3.0': + '@iconify/utils@3.0.1': dependencies: '@antfu/install-pkg': 1.1.0 - '@antfu/utils': 8.1.1 + '@antfu/utils': 9.2.0 '@iconify/types': 2.0.0 debug: 4.4.1 globals: 15.15.0 @@ -3153,7 +3134,7 @@ snapshots: dependencies: string-width: 5.1.2 string-width-cjs: string-width@4.2.3 - strip-ansi: 7.1.0 + strip-ansi: 7.1.2 strip-ansi-cjs: strip-ansi@6.0.1 wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 @@ -3217,7 +3198,7 @@ snapshots: dependencies: langium: 3.3.1 - '@netlify/plugin-nextjs@5.13.0': {} + '@netlify/plugin-nextjs@5.13.1': {} '@next/env@15.4.5': {} @@ -3245,8 +3226,6 @@ snapshots: '@next/swc-win32-x64-msvc@15.4.5': optional: true - '@orama/orama@3.1.12': {} - '@orama/orama@3.1.13': {} '@radix-ui/number@1.1.1': {} @@ -3650,13 +3629,6 @@ snapshots: dependencies: zod: 3.24.1 - '@shikijs/core@3.12.1': - dependencies: - '@shikijs/types': 3.12.1 - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.4 - hast-util-to-html: 9.0.5 - '@shikijs/core@3.12.2': dependencies: '@shikijs/types': 3.12.2 @@ -3664,45 +3636,21 @@ snapshots: '@types/hast': 3.0.4 hast-util-to-html: 9.0.5 - '@shikijs/engine-javascript@3.12.1': - dependencies: - '@shikijs/types': 3.12.1 - '@shikijs/vscode-textmate': 10.0.2 - oniguruma-to-es: 4.3.3 - '@shikijs/engine-javascript@3.12.2': dependencies: '@shikijs/types': 3.12.2 '@shikijs/vscode-textmate': 10.0.2 oniguruma-to-es: 4.3.3 - '@shikijs/engine-oniguruma@3.12.1': - dependencies: - '@shikijs/types': 3.12.1 - '@shikijs/vscode-textmate': 10.0.2 - '@shikijs/engine-oniguruma@3.12.2': dependencies: '@shikijs/types': 3.12.2 '@shikijs/vscode-textmate': 10.0.2 - '@shikijs/langs@3.12.1': - dependencies: - '@shikijs/types': 3.12.1 - '@shikijs/langs@3.12.2': dependencies: '@shikijs/types': 3.12.2 - '@shikijs/rehype@3.12.1': - dependencies: - '@shikijs/types': 3.12.1 - '@types/hast': 3.0.4 - hast-util-to-string: 3.0.1 - shiki: 3.12.1 - unified: 11.0.5 - unist-util-visit: 5.0.0 - '@shikijs/rehype@3.12.2': dependencies: '@shikijs/types': 3.12.2 @@ -3712,29 +3660,15 @@ snapshots: unified: 11.0.5 unist-util-visit: 5.0.0 - '@shikijs/themes@3.12.1': - dependencies: - '@shikijs/types': 3.12.1 - '@shikijs/themes@3.12.2': dependencies: '@shikijs/types': 3.12.2 - '@shikijs/transformers@3.12.1': - dependencies: - '@shikijs/core': 3.12.1 - '@shikijs/types': 3.12.1 - '@shikijs/transformers@3.12.2': dependencies: '@shikijs/core': 3.12.2 '@shikijs/types': 3.12.2 - '@shikijs/types@3.12.1': - dependencies: - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.4 - '@shikijs/types@3.12.2': dependencies: '@shikijs/vscode-textmate': 10.0.2 @@ -3748,77 +3682,77 @@ snapshots: dependencies: tslib: 2.8.1 - '@tailwindcss/node@4.1.12': + '@tailwindcss/node@4.1.13': dependencies: '@jridgewell/remapping': 2.3.5 enhanced-resolve: 5.18.3 jiti: 2.5.1 lightningcss: 1.30.1 - magic-string: 0.30.18 + magic-string: 0.30.19 source-map-js: 1.2.1 - tailwindcss: 4.1.12 + tailwindcss: 4.1.13 - '@tailwindcss/oxide-android-arm64@4.1.12': + '@tailwindcss/oxide-android-arm64@4.1.13': optional: true - '@tailwindcss/oxide-darwin-arm64@4.1.12': + '@tailwindcss/oxide-darwin-arm64@4.1.13': optional: true - '@tailwindcss/oxide-darwin-x64@4.1.12': + '@tailwindcss/oxide-darwin-x64@4.1.13': optional: true - '@tailwindcss/oxide-freebsd-x64@4.1.12': + '@tailwindcss/oxide-freebsd-x64@4.1.13': optional: true - '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.12': + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.13': optional: true - '@tailwindcss/oxide-linux-arm64-gnu@4.1.12': + '@tailwindcss/oxide-linux-arm64-gnu@4.1.13': optional: true - '@tailwindcss/oxide-linux-arm64-musl@4.1.12': + '@tailwindcss/oxide-linux-arm64-musl@4.1.13': optional: true - '@tailwindcss/oxide-linux-x64-gnu@4.1.12': + '@tailwindcss/oxide-linux-x64-gnu@4.1.13': optional: true - '@tailwindcss/oxide-linux-x64-musl@4.1.12': + '@tailwindcss/oxide-linux-x64-musl@4.1.13': optional: true - '@tailwindcss/oxide-wasm32-wasi@4.1.12': + '@tailwindcss/oxide-wasm32-wasi@4.1.13': optional: true - '@tailwindcss/oxide-win32-arm64-msvc@4.1.12': + '@tailwindcss/oxide-win32-arm64-msvc@4.1.13': optional: true - '@tailwindcss/oxide-win32-x64-msvc@4.1.12': + '@tailwindcss/oxide-win32-x64-msvc@4.1.13': optional: true - '@tailwindcss/oxide@4.1.12': + '@tailwindcss/oxide@4.1.13': dependencies: detect-libc: 2.0.4 tar: 7.4.3 optionalDependencies: - '@tailwindcss/oxide-android-arm64': 4.1.12 - '@tailwindcss/oxide-darwin-arm64': 4.1.12 - '@tailwindcss/oxide-darwin-x64': 4.1.12 - '@tailwindcss/oxide-freebsd-x64': 4.1.12 - '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.12 - '@tailwindcss/oxide-linux-arm64-gnu': 4.1.12 - '@tailwindcss/oxide-linux-arm64-musl': 4.1.12 - '@tailwindcss/oxide-linux-x64-gnu': 4.1.12 - '@tailwindcss/oxide-linux-x64-musl': 4.1.12 - '@tailwindcss/oxide-wasm32-wasi': 4.1.12 - '@tailwindcss/oxide-win32-arm64-msvc': 4.1.12 - '@tailwindcss/oxide-win32-x64-msvc': 4.1.12 - - '@tailwindcss/postcss@4.1.12': + '@tailwindcss/oxide-android-arm64': 4.1.13 + '@tailwindcss/oxide-darwin-arm64': 4.1.13 + '@tailwindcss/oxide-darwin-x64': 4.1.13 + '@tailwindcss/oxide-freebsd-x64': 4.1.13 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.13 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.13 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.13 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.13 + '@tailwindcss/oxide-linux-x64-musl': 4.1.13 + '@tailwindcss/oxide-wasm32-wasi': 4.1.13 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.13 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.13 + + '@tailwindcss/postcss@4.1.13': dependencies: '@alloc/quick-lru': 5.2.0 - '@tailwindcss/node': 4.1.12 - '@tailwindcss/oxide': 4.1.12 + '@tailwindcss/node': 4.1.13 + '@tailwindcss/oxide': 4.1.13 postcss: 8.5.6 - tailwindcss: 4.1.12 + tailwindcss: 4.1.13 '@types/d3-array@3.2.1': {} @@ -4009,13 +3943,13 @@ snapshots: ansi-regex@5.0.1: {} - ansi-regex@6.2.0: {} + ansi-regex@6.2.2: {} ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 - ansi-styles@6.2.1: {} + ansi-styles@6.2.3: {} argparse@1.0.10: dependencies: @@ -4031,7 +3965,7 @@ snapshots: bail@2.0.2: {} - caniuse-lite@1.0.30001739: {} + caniuse-lite@1.0.30001741: {} ccount@2.0.1: {} @@ -4487,9 +4421,9 @@ snapshots: fumadocs-core@15.7.7(@types/react@19.1.12)(next@15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1): dependencies: '@formatjs/intl-localematcher': 0.6.1 - '@orama/orama': 3.1.12 - '@shikijs/rehype': 3.12.1 - '@shikijs/transformers': 3.12.1 + '@orama/orama': 3.1.13 + '@shikijs/rehype': 3.12.2 + '@shikijs/transformers': 3.12.2 github-slugger: 2.0.0 hast-util-to-estree: 3.1.3 hast-util-to-jsx-runtime: 2.3.6 @@ -4520,12 +4454,12 @@ snapshots: estree-util-value-to-estree: 3.4.0 fumadocs-core: 15.7.7(@types/react@19.1.12)(next@15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1) js-yaml: 4.1.0 - lru-cache: 11.1.0 + lru-cache: 11.2.1 picocolors: 1.1.1 remark-mdx: 3.1.1 remark-parse: 11.0.0 tinyexec: 1.0.1 - tinyglobby: 0.2.14 + tinyglobby: 0.2.15 unified: 11.0.5 unist-util-visit: 5.0.0 zod: 4.1.5 @@ -4536,7 +4470,7 @@ snapshots: transitivePeerDependencies: - supports-color - fumadocs-openapi@9.3.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(next@15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(tailwindcss@4.1.12): + fumadocs-openapi@9.3.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(next@15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(tailwindcss@4.1.13): dependencies: '@fumari/json-schema-to-typescript': 1.1.3 '@radix-ui/react-accordion': 1.2.12(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) @@ -4547,7 +4481,7 @@ snapshots: ajv: 8.17.1 class-variance-authority: 0.7.1 fumadocs-core: 15.7.10(@types/react@19.1.12)(next@15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - fumadocs-ui: 15.7.10(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(next@15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(tailwindcss@4.1.12) + fumadocs-ui: 15.7.10(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(next@15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(tailwindcss@4.1.13) github-slugger: 2.0.0 gray-matter: 4.0.3 hast-util-to-jsx-runtime: 2.3.6 @@ -4560,7 +4494,7 @@ snapshots: remark: 15.0.1 remark-rehype: 11.1.2 shiki: 3.12.2 - tinyglobby: 0.2.14 + tinyglobby: 0.2.15 xml-js: 1.6.11 optionalDependencies: '@types/react': 19.1.12 @@ -4576,7 +4510,7 @@ snapshots: - tailwindcss - waku - fumadocs-ui@15.7.10(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(next@15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(tailwindcss@4.1.12): + fumadocs-ui@15.7.10(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(next@15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(tailwindcss@4.1.13): dependencies: '@radix-ui/react-accordion': 1.2.12(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) @@ -4601,7 +4535,7 @@ snapshots: optionalDependencies: '@types/react': 19.1.12 next: 15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - tailwindcss: 4.1.12 + tailwindcss: 4.1.13 transitivePeerDependencies: - '@mixedbread/sdk' - '@oramacloud/client' @@ -4612,7 +4546,7 @@ snapshots: - supports-color - waku - fumadocs-ui@15.7.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(next@15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(tailwindcss@4.1.12): + fumadocs-ui@15.7.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(next@15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(tailwindcss@4.1.13): dependencies: '@radix-ui/react-accordion': 1.2.12(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) @@ -4637,7 +4571,7 @@ snapshots: optionalDependencies: '@types/react': 19.1.12 next: 15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - tailwindcss: 4.1.12 + tailwindcss: 4.1.13 transitivePeerDependencies: - '@mixedbread/sdk' - '@oramacloud/client' @@ -4931,13 +4865,13 @@ snapshots: longest-streak@3.1.0: {} - lru-cache@11.1.0: {} + lru-cache@11.2.1: {} lucide-react@0.540.0(react@19.1.1): dependencies: react: 19.1.1 - magic-string@0.30.18: + magic-string@0.30.19: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -4945,7 +4879,7 @@ snapshots: markdown-table@3.0.4: {} - marked@16.2.1: {} + marked@15.0.12: {} mdast-util-find-and-replace@3.0.2: dependencies: @@ -5122,10 +5056,10 @@ snapshots: dependencies: '@types/mdast': 4.0.4 - mermaid@11.10.1: + mermaid@11.11.0: dependencies: '@braintree/sanitize-url': 7.1.1 - '@iconify/utils': 2.3.0 + '@iconify/utils': 3.0.1 '@mermaid-js/parser': 0.6.2 '@types/d3': 7.4.3 cytoscape: 3.33.1 @@ -5139,7 +5073,7 @@ snapshots: katex: 0.16.22 khroma: 2.1.0 lodash-es: 4.17.21 - marked: 16.2.1 + marked: 15.0.12 roughjs: 4.6.6 stylis: 4.3.6 ts-dedent: 2.2.0 @@ -5455,7 +5389,7 @@ snapshots: dependencies: '@next/env': 15.4.5 '@swc/helpers': 0.5.15 - caniuse-lite: 1.0.30001739 + caniuse-lite: 1.0.30001741 postcss: 8.4.31 react: 19.1.1 react-dom: 19.1.1(react@19.1.1) @@ -5514,7 +5448,7 @@ snapshots: path-scurry@2.0.0: dependencies: - lru-cache: 11.1.0 + lru-cache: 11.2.1 minipass: 7.1.2 pathe@2.0.3: {} @@ -5793,17 +5727,6 @@ snapshots: shebang-regex@3.0.0: {} - shiki@3.12.1: - dependencies: - '@shikijs/core': 3.12.1 - '@shikijs/engine-javascript': 3.12.1 - '@shikijs/engine-oniguruma': 3.12.1 - '@shikijs/langs': 3.12.1 - '@shikijs/themes': 3.12.1 - '@shikijs/types': 3.12.1 - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.4 - shiki@3.12.2: dependencies: '@shikijs/core': 3.12.2 @@ -5840,7 +5763,7 @@ snapshots: dependencies: eastasianwidth: 0.2.0 emoji-regex: 9.2.2 - strip-ansi: 7.1.0 + strip-ansi: 7.1.2 stringify-entities@4.0.4: dependencies: @@ -5851,9 +5774,9 @@ snapshots: dependencies: ansi-regex: 5.0.1 - strip-ansi@7.1.0: + strip-ansi@7.1.2: dependencies: - ansi-regex: 6.2.0 + ansi-regex: 6.2.2 strip-bom-string@1.0.0: {} @@ -5876,7 +5799,7 @@ snapshots: tailwind-merge@3.3.1: {} - tailwindcss@4.1.12: {} + tailwindcss@4.1.13: {} tapable@2.2.3: {} @@ -5891,7 +5814,7 @@ snapshots: tinyexec@1.0.1: {} - tinyglobby@0.2.14: + tinyglobby@0.2.15: dependencies: fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 @@ -5904,7 +5827,7 @@ snapshots: tslib@2.8.1: {} - tw-animate-css@1.3.7: {} + tw-animate-css@1.3.8: {} typescript@5.9.2: {} @@ -6024,9 +5947,9 @@ snapshots: wrap-ansi@8.1.0: dependencies: - ansi-styles: 6.2.1 + ansi-styles: 6.2.3 string-width: 5.1.2 - strip-ansi: 7.1.0 + strip-ansi: 7.1.2 xml-js@1.6.11: dependencies: diff --git a/src/app/(docs)/[...slug]/layout.tsx b/src/app/(docs)/[...slug]/layout.tsx index 7ba432d7..a0815b85 100644 --- a/src/app/(docs)/[...slug]/layout.tsx +++ b/src/app/(docs)/[...slug]/layout.tsx @@ -1,7 +1,7 @@ -import { DocsLayout } from "fumadocs-ui/layouts/docs"; +import { DocsLayout } from "fumadocs-ui/layouts/notebook"; import type { ReactNode } from "react"; - import { baseOptions } from "@/app/layout.config"; + import { ArbitrumIcon, EthereumIcon, @@ -58,6 +58,7 @@ export default async function Layout({ return ( ; + +export function LanguageToggle(props: LanguageSelectProps): React.ReactElement { + const context = useI18n(); + if (!context.locales) throw new Error('Missing ``'); + + return ( + + + {props.children} + + +

+ {context.text.chooseLanguage} +

+ {context.locales.map((item) => ( + + ))} +
+
+ ); +} + +export function LanguageToggleText( + props: HTMLAttributes, +): React.ReactElement { + const context = useI18n(); + const text = context.locales?.find( + (item) => item.locale === context.locale, + )?.name; + + return {text}; +} diff --git a/src/components/layout/docs/client.tsx b/src/components/layout/docs/client.tsx new file mode 100644 index 00000000..fd6b25a9 --- /dev/null +++ b/src/components/layout/docs/client.tsx @@ -0,0 +1,136 @@ +'use client'; +import { cn } from '../../../lib/cn'; +import { type ComponentProps, useMemo } from 'react'; +import { useSidebar } from 'fumadocs-ui/contexts/sidebar'; +import { useNav } from 'fumadocs-ui/contexts/layout'; +import { buttonVariants } from '../../ui/button'; +import { Sidebar as SidebarIcon } from 'lucide-react'; +import Link from 'fumadocs-core/link'; +import { usePathname } from 'fumadocs-core/framework'; +import { isTabActive } from '../../../lib/is-active'; +import type { Option } from '../../root-toggle'; + +export function Navbar({ + mode, + ...props +}: ComponentProps<'header'> & { mode: 'top' | 'auto' }) { + const { open, collapsed } = useSidebar(); + const { isTransparent } = useNav(); + + return ( +
+ {props.children} +
+ ); +} + +export function LayoutBody(props: ComponentProps<'main'>) { + const { collapsed } = useSidebar(); + + return ( +
+ {props.children} +
+ ); +} + +export function NavbarSidebarTrigger({ + className, + ...props +}: ComponentProps<'button'>) { + const { setOpen } = useSidebar(); + + return ( + + ); +} + +export function LayoutTabs({ + options, + ...props +}: ComponentProps<'div'> & { + options: Option[]; +}) { + const pathname = usePathname(); + const selected = useMemo(() => { + return options.findLast((option) => isTabActive(option, pathname)); + }, [options, pathname]); + + return ( +
+ {options.map((option) => ( + + ))} +
+ ); +} + +function LayoutTab({ + option: { title, url, unlisted, props }, + selected = false, +}: { + option: Option; + selected?: boolean; +}) { + return ( + + {title} + + ); +} diff --git a/src/components/layout/docs/index.tsx b/src/components/layout/docs/index.tsx new file mode 100644 index 00000000..6a13c0f9 --- /dev/null +++ b/src/components/layout/docs/index.tsx @@ -0,0 +1,574 @@ +import { + type ComponentProps, + Fragment, + type HTMLAttributes, + type ReactNode, + useMemo, +} from 'react'; +import { + type BaseLayoutProps, + BaseLinkItem, + type BaseLinkType, + getLinks, + type LinkItemType, +} from '../shared/index'; +import { + Sidebar, + SidebarCollapseTrigger, + type SidebarComponents, + SidebarContent, + SidebarContentMobile, + SidebarFolder, + SidebarFolderContent, + SidebarFolderLink, + SidebarFolderTrigger, + SidebarFooter, + SidebarHeader, + SidebarItem, + SidebarPageTree, + type SidebarProps, + SidebarTrigger, + SidebarViewport, +} from '../../sidebar'; +import { TreeContextProvider } from 'fumadocs-ui/contexts/tree'; +import { cn } from '../../../lib/cn'; +import { buttonVariants } from '../../ui/button'; +import { + ChevronDown, + Languages, + Sidebar as SidebarIcon, + X, +} from 'lucide-react'; +import { LanguageToggle } from '../../language-toggle'; +import { ThemeToggle } from '../../theme-toggle'; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from '../../ui/popover'; +import type { PageTree } from 'fumadocs-core/server'; +import { + LayoutBody, + LayoutTabs, + Navbar, + NavbarSidebarTrigger, +} from './client'; +import { NavProvider } from 'fumadocs-ui/contexts/layout'; +import { type Option, RootToggle } from '../../root-toggle'; +import Link from 'fumadocs-core/link'; +import { + LargeSearchToggle, + SearchToggle, +} from '../../search-toggle'; +import { HideIfEmpty } from 'fumadocs-core/hide-if-empty'; +import { + getSidebarTabs, + type GetSidebarTabsOptions, +} from 'fumadocs-ui/utils/get-sidebar-tabs'; + +export interface DocsLayoutProps extends BaseLayoutProps { + tree: PageTree.Root; + tabMode?: 'sidebar' | 'navbar'; + + nav?: BaseLayoutProps['nav'] & { + mode?: 'top' | 'auto'; + }; + + sidebar?: SidebarOptions; + + containerProps?: HTMLAttributes; +} + +interface SidebarOptions + extends ComponentProps<'aside'>, + Pick { + components?: Partial; + + /** + * Root Toggle options + */ + tabs?: Option[] | GetSidebarTabsOptions | false; + + banner?: ReactNode; + footer?: ReactNode; + + /** + * Support collapsing the sidebar on desktop mode + * + * @defaultValue true + */ + collapsible?: boolean; +} + +export function DocsLayout(props: DocsLayoutProps) { + const { + tabMode = 'sidebar', + nav: { transparentMode, ...nav } = {}, + sidebar: { tabs: tabOptions, ...sidebarProps } = {}, + i18n = false, + disableThemeSwitch = false, + themeSwitch = { enabled: !disableThemeSwitch }, + } = props; + + const navMode = nav.mode ?? 'auto'; + const links = getLinks(props.links ?? [], props.githubUrl); + const tabs = useMemo(() => { + if (Array.isArray(tabOptions)) { + return tabOptions; + } + + if (typeof tabOptions === 'object') { + return getSidebarTabs(props.tree, tabOptions); + } + + if (tabOptions !== false) { + return getSidebarTabs(props.tree); + } + + return []; + }, [tabOptions, props.tree]); + + function sidebar() { + const { + banner, + footer, + components, + collapsible = true, + prefetch, + defaultOpenLevel, + ...rest + } = sidebarProps; + const iconLinks = links.filter((item) => item.type === 'icon'); + + const rootToggle = ( + <> + {tabMode === 'sidebar' && tabs.length > 0 && ( + + )} + {tabMode === 'navbar' && tabs.length > 0 && ( + + )} + + ); + + const sidebarNav = ( +
+ + {nav.title} + + {collapsible && ( + + + + )} +
+ ); + + const viewport = ( + + {links + .filter((item) => item.type !== 'icon') + .map((item, i, arr) => ( + + ))} + + + + ); + + const content = ( + + + {navMode === 'auto' && sidebarNav} + {nav.children} + {banner} + {rootToggle} + + {viewport} + + {iconLinks.map((item, i) => ( + + {item.icon} + + ))} + {footer} + + + ); + + const mobile = ( + + + + + + {banner} + {rootToggle} + + {viewport} + + {iconLinks.map((item, i) => ( + + {item.icon} + + ))} + {i18n ? ( + + + + ) : null} + {themeSwitch.enabled !== false && + (themeSwitch.component ?? ( + + ))} + {footer} + + + ); + + return ( + + ); + } + + return ( + + + + {sidebar()} + + {props.children} + + + + ); +} + +function DocsNavbar({ + links, + tabs, + searchToggle = {}, + themeSwitch = {}, + nav = {}, + ...props +}: DocsLayoutProps & { + links: LinkItemType[]; + tabs: Option[]; +}) { + const navMode = nav.mode ?? 'auto'; + const sidebarCollapsible = props.sidebar?.collapsible ?? true; + + return ( + 0 && 'lg:on-root:[--fd-nav-height:104px]', + )} + > +
+
+ {sidebarCollapsible && navMode === 'auto' && ( + + + + )} + + {nav.title} + +
+ {searchToggle.enabled !== false && + (searchToggle.components?.lg ? ( +
+ {searchToggle.components.lg} +
+ ) : ( + + ))} +
+
+ {links + .filter((item) => item.type !== 'icon') + .map((item, i) => ( + + ))} +
+ {nav.children} + {links + .filter((item) => item.type === 'icon') + .map((item, i) => ( + + {item.icon} + + ))} + +
+ {searchToggle.enabled !== false && + (searchToggle.components?.sm ?? ( + + ))} + +
+ +
+ {props.i18n ? ( + + + + ) : null} + {themeSwitch.enabled !== false && + (themeSwitch.component ?? ( + + ))} + {sidebarCollapsible && navMode === 'top' && ( + + + + )} +
+
+
+ {tabs.length > 0 && ( + + )} +
+ ); +} + +function NavbarLinkItem({ + item, + ...props +}: { item: LinkItemType } & HTMLAttributes) { + if (item.type === 'menu') { + return ( + + + {item.url ? ( + {item.text} + ) : ( + item.text + )} + + + + {item.items.map((child, i) => { + if (child.type === 'custom') + return {child.children}; + + return ( + + {child.icon} + {child.text} + + ); + })} + + + ); + } + + if (item.type === 'custom') return item.children; + + return ( + + {item.text} + + ); +} + +function SidebarLinkItem({ + item, + ...props +}: { + item: Exclude; + className?: string; +}) { + if (item.type === 'menu') + return ( + + {item.url ? ( + + {item.icon} + {item.text} + + ) : ( + + {item.icon} + {item.text} + + )} + + {item.items.map((child, i) => ( + + ))} + + + ); + + if (item.type === 'custom') return
{item.children}
; + + return ( + + {item.text} + + ); +} + +export { Navbar, NavbarSidebarTrigger }; diff --git a/src/components/layout/docs/page-client.tsx b/src/components/layout/docs/page-client.tsx new file mode 100644 index 00000000..e84846b4 --- /dev/null +++ b/src/components/layout/docs/page-client.tsx @@ -0,0 +1,417 @@ +'use client'; + +import { + type ComponentProps, + Fragment, + useEffect, + useMemo, + useRef, + useState, +} from 'react'; +import { ChevronDown, ChevronLeft, ChevronRight } from 'lucide-react'; +import Link from 'fumadocs-core/link'; +import { cn } from '../../../lib/cn'; +import { useI18n } from 'fumadocs-ui/contexts/i18n'; +import { useTreeContext, useTreePath } from 'fumadocs-ui/contexts/tree'; +import type { PageTree } from 'fumadocs-core/server'; +import { createContext, usePathname } from 'fumadocs-core/framework'; +import { + type BreadcrumbOptions, + getBreadcrumbItemsFromPath, +} from 'fumadocs-core/breadcrumb'; +import { useNav } from 'fumadocs-ui/contexts/layout'; +import { isActive } from '../../../lib/is-active'; +import { useEffectEvent } from 'fumadocs-core/utils/use-effect-event'; +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from '../../ui/collapsible'; +import { useSidebar } from 'fumadocs-ui/contexts/sidebar'; +import { useTOCItems } from '../../ui/toc'; +import { useActiveAnchor } from 'fumadocs-core/toc'; + +const TocPopoverContext = createContext<{ + open: boolean; + setOpen: (open: boolean) => void; +}>('TocPopoverContext'); + +export function PageTOCPopoverTrigger(props: ComponentProps<'button'>) { + const { text } = useI18n(); + const { open } = TocPopoverContext.use(); + const items = useTOCItems(); + const active = useActiveAnchor(); + const selected = useMemo( + () => items.findIndex((item) => active === item.url.slice(1)), + [items, active], + ); + const path = useTreePath().at(-1); + const showItem = selected !== -1 && !open; + + return ( + + + + + {path?.name ?? text.toc} + + + {items[selected]?.title} + + + + + ); +} + +interface ProgressCircleProps + extends Omit, 'strokeWidth'> { + value: number; + strokeWidth?: number; + size?: number; + min?: number; + max?: number; +} + +function clamp(input: number, min: number, max: number): number { + if (input < min) return min; + if (input > max) return max; + return input; +} + +function ProgressCircle({ + value, + strokeWidth = 2, + size = 24, + min = 0, + max = 100, + ...restSvgProps +}: ProgressCircleProps) { + const normalizedValue = clamp(value, min, max); + const radius = (size - strokeWidth) / 2; + const circumference = 2 * Math.PI * radius; + const progress = (normalizedValue / max) * circumference; + const circleProps = { + cx: size / 2, + cy: size / 2, + r: radius, + fill: 'none', + strokeWidth, + }; + + return ( + + + + + ); +} + +export function PageTOCPopoverContent(props: ComponentProps<'div'>) { + return ( + + {props.children} + + ); +} + +export function PageTOCPopover(props: ComponentProps<'div'>) { + const ref = useRef(null); + const [open, setOpen] = useState(false); + const { collapsed } = useSidebar(); + const { isTransparent } = useNav(); + + const onClick = useEffectEvent((e: Event) => { + if (!open) return; + + if (ref.current && !ref.current.contains(e.target as HTMLElement)) + setOpen(false); + }); + + useEffect(() => { + window.addEventListener('click', onClick); + + return () => { + window.removeEventListener('click', onClick); + }; + }, [onClick]); + + return ( + ({ + open, + setOpen, + }), + [setOpen, open], + )} + > + +
+ {props.children} +
+
+
+ ); +} + +export function PageLastUpdate({ + date: value, + ...props +}: Omit, 'children'> & { date: Date | string }) { + const { text } = useI18n(); + const [date, setDate] = useState(''); + + useEffect(() => { + // to the timezone of client + setDate(new Date(value).toLocaleDateString()); + }, [value]); + + return ( +

+ {text.lastUpdate} {date} +

+ ); +} + +type Item = Pick; +export interface FooterProps extends ComponentProps<'div'> { + /** + * Items including information for the next and previous page + */ + items?: { + previous?: Item; + next?: Item; + }; +} + +function scanNavigationList(tree: PageTree.Node[]) { + const list: PageTree.Item[] = []; + + tree.forEach((node) => { + if (node.type === 'folder') { + if (node.index) { + list.push(node.index); + } + + list.push(...scanNavigationList(node.children)); + return; + } + + if (node.type === 'page' && !node.external) { + list.push(node); + } + }); + + return list; +} + +const listCache = new Map(); + +export function PageFooter({ items, ...props }: FooterProps) { + const { root } = useTreeContext(); + const pathname = usePathname(); + + const { previous, next } = useMemo(() => { + if (items) return items; + + const cached = listCache.get(root.$id); + const list = cached ?? scanNavigationList(root.children); + listCache.set(root.$id, list); + + const idx = list.findIndex((item) => isActive(item.url, pathname, false)); + + if (idx === -1) return {}; + return { + previous: list[idx - 1], + next: list[idx + 1], + }; + }, [items, pathname, root]); + + return ( +
+ {previous ? : null} + {next ? : null} +
+ ); +} + +function FooterItem({ item, index }: { item: Item; index: 0 | 1 }) { + const { text } = useI18n(); + const Icon = index === 0 ? ChevronLeft : ChevronRight; + + return ( + +
+ +

{item.name}

+
+

+ {item.description ?? (index === 0 ? text.previousPage : text.nextPage)} +

+ + ); +} + +export type BreadcrumbProps = BreadcrumbOptions & ComponentProps<'div'>; + +export function PageBreadcrumb({ + includeRoot = false, + includeSeparator, + includePage = false, + ...props +}: BreadcrumbProps) { + const path = useTreePath(); + const { root } = useTreeContext(); + const items = useMemo(() => { + return getBreadcrumbItemsFromPath(root, path, { + includePage, + includeSeparator, + includeRoot, + }); + }, [includePage, includeRoot, includeSeparator, path, root]); + + if (items.length === 0) return null; + + return ( +
+ {items.map((item, i) => { + const className = cn( + 'truncate', + i === items.length - 1 && 'text-fd-primary font-medium', + ); + + return ( + + {i !== 0 && } + {item.url ? ( + + {item.name} + + ) : ( + {item.name} + )} + + ); + })} +
+ ); +} + +export function PageTOC(props: ComponentProps<'div'>) { + const { collapsed } = useSidebar(); + const offset = collapsed ? '0px' : 'var(--fd-layout-offset)'; + + return ( +
+
+ {props.children} +
+
+ ); +} diff --git a/src/components/layout/docs/page.tsx b/src/components/layout/docs/page.tsx new file mode 100644 index 00000000..38b85f9c --- /dev/null +++ b/src/components/layout/docs/page.tsx @@ -0,0 +1,103 @@ +import { type ComponentProps } from 'react'; +import { cn } from '../../../lib/cn'; +import { + type BreadcrumbProps, + type FooterProps, + PageBreadcrumb, + PageFooter, + PageLastUpdate, + PageTOC, + PageTOCPopover, + PageTOCPopoverContent, + PageTOCPopoverTrigger, +} from './page-client'; +import { TOCItems, TOCProvider, TOCScrollArea } from '../../ui/toc'; +import { Text } from 'lucide-react'; +import { I18nLabel } from 'fumadocs-ui/contexts/i18n'; +import ClerkTOCItems from '../../ui/toc-clerk'; +import type { AnchorProviderProps } from 'fumadocs-core/toc'; + +export function PageTOCTitle(props: ComponentProps<'h2'>) { + return ( +

+ + +

+ ); +} + +export function PageTOCItems({ + variant = 'normal', + ...props +}: ComponentProps<'div'> & { variant?: 'clerk' | 'normal' }) { + return ( + + {variant === 'clerk' ? : } + + ); +} + +export function PageTOCPopoverItems({ + variant = 'normal', + ...props +}: ComponentProps<'div'> & { variant?: 'clerk' | 'normal' }) { + return ( + + {variant === 'clerk' ? : } + + ); +} + +export function PageArticle(props: ComponentProps<'article'>) { + return ( +
+ {props.children} +
+ ); +} + +export interface RootProps extends ComponentProps<'div'> { + toc?: Omit | false; +} + +export function PageRoot({ toc = false, children, ...props }: RootProps) { + const content = ( +
+ {children} +
+ ); + + if (toc) return {content}; + return content; +} + +export { + PageBreadcrumb, + PageFooter, + PageLastUpdate, + PageTOC, + PageTOCPopover, + PageTOCPopoverTrigger, + PageTOCPopoverContent, + type FooterProps, + type BreadcrumbProps, +}; diff --git a/src/components/layout/page.tsx b/src/components/layout/page.tsx new file mode 100644 index 00000000..05bccbed --- /dev/null +++ b/src/components/layout/page.tsx @@ -0,0 +1,288 @@ +import { type ComponentProps, forwardRef, type ReactNode } from 'react'; +import { cn } from '../../lib/cn'; +import { buttonVariants } from '../ui/button'; +import { Edit } from 'lucide-react'; +import { I18nLabel } from 'fumadocs-ui/contexts/i18n'; +import { + type BreadcrumbProps, + type FooterProps, + PageArticle, + PageBreadcrumb, + PageFooter, + PageLastUpdate, + PageRoot, + PageTOC, + PageTOCItems, + PageTOCPopover, + PageTOCPopoverContent, + PageTOCPopoverItems, + PageTOCPopoverTrigger, + PageTOCTitle, +} from './docs/page'; +import type { AnchorProviderProps } from 'fumadocs-core/toc'; +import type { TOCItemType } from 'fumadocs-core/server'; + +interface EditOnGitHubOptions + extends Omit, 'href' | 'children'> { + owner: string; + repo: string; + + /** + * SHA or ref (branch or tag) name. + * + * @defaultValue main + */ + sha?: string; + + /** + * File path in the repo + */ + path: string; +} + +interface BreadcrumbOptions extends BreadcrumbProps { + enabled: boolean; + component: ReactNode; + + /** + * Show the full path to the current page + * + * @defaultValue false + * @deprecated use `includePage` instead + */ + full?: boolean; +} + +interface FooterOptions extends FooterProps { + enabled: boolean; + component: ReactNode; +} + +export interface DocsPageProps { + toc?: TOCItemType[]; + tableOfContent?: Partial; + tableOfContentPopover?: Partial; + + /** + * Extend the page to fill all available space + * + * @defaultValue false + */ + full?: boolean; + + /** + * Replace or disable breadcrumb + */ + breadcrumb?: Partial; + + /** + * Footer navigation, you can disable it by passing `false` + */ + footer?: Partial; + + editOnGithub?: EditOnGitHubOptions; + lastUpdate?: Date | string | number; + + container?: ComponentProps<'div'>; + article?: ComponentProps<'article'>; + children?: ReactNode; +} + +type TableOfContentOptions = Pick & { + /** + * Custom content in TOC container, before the main TOC + */ + header?: ReactNode; + + /** + * Custom content in TOC container, after the main TOC + */ + footer?: ReactNode; + + enabled: boolean; + component: ReactNode; + + /** + * @defaultValue 'normal' + */ + style?: 'normal' | 'clerk'; +}; + +type TableOfContentPopoverOptions = Omit; + +export function DocsPage({ + editOnGithub, + breadcrumb: { + enabled: breadcrumbEnabled = true, + component: breadcrumb, + ...breadcrumbProps + } = {}, + footer = {}, + lastUpdate, + container, + full = false, + tableOfContentPopover: { + enabled: tocPopoverEnabled, + component: tocPopover, + ...tocPopoverOptions + } = {}, + tableOfContent: { + enabled: tocEnabled, + component: tocReplace, + ...tocOptions + } = {}, + toc = [], + article, + children, +}: DocsPageProps) { + // disable TOC on full mode, you can still enable it with `enabled` option. + tocEnabled ??= + !full && + (toc.length > 0 || + tocOptions.footer !== undefined || + tocOptions.header !== undefined); + + tocPopoverEnabled ??= + toc.length > 0 || + tocPopoverOptions.header !== undefined || + tocPopoverOptions.footer !== undefined; + + return ( + + {tocPopoverEnabled && + (tocPopover ?? ( + + + + {tocPopoverOptions.header} + + {tocPopoverOptions.footer} + + + ))} + + {breadcrumbEnabled && + (breadcrumb ?? )} + {children} +
+ {editOnGithub && ( + + )} + {lastUpdate && } +
+ {footer.enabled !== false && + (footer.component ?? )} +
+ {tocEnabled && + (tocReplace ?? ( + + {tocOptions.header} + + + {tocOptions.footer} + + ))} +
+ ); +} + +export function EditOnGitHub(props: ComponentProps<'a'>) { + return ( + + {props.children ?? ( + <> + + + + )} + + ); +} + +/** + * Add typography styles + */ +export const DocsBody = forwardRef>( + (props, ref) => ( +
+ {props.children} +
+ ), +); + +DocsBody.displayName = 'DocsBody'; + +export const DocsDescription = forwardRef< + HTMLParagraphElement, + ComponentProps<'p'> +>((props, ref) => { + // don't render if no description provided + if (props.children === undefined) return null; + + return ( +

+ {props.children} +

+ ); +}); + +DocsDescription.displayName = 'DocsDescription'; + +export const DocsTitle = forwardRef>( + (props, ref) => { + return ( +

+ {props.children} +

+ ); + }, +); + +DocsTitle.displayName = 'DocsTitle'; + +/** + * For separate MDX page + */ +export function withArticle(props: ComponentProps<'main'>): ReactNode { + return ( +
+
{props.children}
+
+ ); +} diff --git a/src/components/layout/shared/client.tsx b/src/components/layout/shared/client.tsx new file mode 100644 index 00000000..d685015e --- /dev/null +++ b/src/components/layout/shared/client.tsx @@ -0,0 +1,30 @@ +'use client'; +import type { ComponentProps } from 'react'; +import { usePathname } from 'fumadocs-core/framework'; +import { isActive } from '../../../lib/is-active'; +import Link from 'fumadocs-core/link'; +import type { BaseLinkType } from './index'; + +export function BaseLinkItem({ + ref, + item, + ...props +}: Omit, 'href'> & { item: BaseLinkType }) { + const pathname = usePathname(); + const activeType = item.active ?? 'url'; + const active = + activeType !== 'none' && + isActive(item.url, pathname, activeType === 'nested-url'); + + return ( + + {props.children} + + ); +} diff --git a/src/components/layout/shared/index.tsx b/src/components/layout/shared/index.tsx new file mode 100644 index 00000000..c73090b6 --- /dev/null +++ b/src/components/layout/shared/index.tsx @@ -0,0 +1,182 @@ +import type { HTMLAttributes, ReactNode } from 'react'; +import type { NavProviderProps } from 'fumadocs-ui/contexts/layout'; +import type { I18nConfig } from 'fumadocs-core/i18n'; + +export interface NavOptions extends NavProviderProps { + enabled: boolean; + component: ReactNode; + + title?: ReactNode; + + /** + * Redirect url of title + * @defaultValue '/' + */ + url?: string; + + children?: ReactNode; +} + +export interface BaseLayoutProps { + themeSwitch?: { + enabled?: boolean; + component?: ReactNode; + mode?: 'light-dark' | 'light-dark-system'; + }; + + searchToggle?: Partial<{ + enabled: boolean; + components: Partial<{ + sm: ReactNode; + lg: ReactNode; + }>; + }>; + + /** + * Remove theme switcher component + * + * @deprecated Use `themeSwitch.enabled` instead. + */ + disableThemeSwitch?: boolean; + + /** + * I18n options + * + * @defaultValue false + */ + i18n?: boolean | I18nConfig; + + /** + * GitHub url + */ + githubUrl?: string; + + links?: LinkItemType[]; + /** + * Replace or disable navbar + */ + nav?: Partial; + + children?: ReactNode; +} + +interface BaseItem { + /** + * Restrict where the item is displayed + * + * @defaultValue 'all' + */ + on?: 'menu' | 'nav' | 'all'; +} + +export interface BaseLinkType extends BaseItem { + url: string; + /** + * When the item is marked as active + * + * @defaultValue 'url' + */ + active?: 'url' | 'nested-url' | 'none'; + external?: boolean; +} + +export interface MainItemType extends BaseLinkType { + type?: 'main'; + icon?: ReactNode; + text: ReactNode; + description?: ReactNode; +} + +export interface IconItemType extends BaseLinkType { + type: 'icon'; + /** + * `aria-label` of icon button + */ + label?: string; + icon: ReactNode; + text: ReactNode; + /** + * @defaultValue true + */ + secondary?: boolean; +} + +export interface ButtonItemType extends BaseLinkType { + type: 'button'; + icon?: ReactNode; + text: ReactNode; + /** + * @defaultValue false + */ + secondary?: boolean; +} + +export interface MenuItemType extends Partial { + type: 'menu'; + icon?: ReactNode; + text: ReactNode; + + items: ( + | (MainItemType & { + /** + * Options when displayed on navigation menu + */ + menu?: HTMLAttributes & { + banner?: ReactNode; + }; + }) + | CustomItemType + )[]; + + /** + * @defaultValue false + */ + secondary?: boolean; +} + +export interface CustomItemType extends BaseItem { + type: 'custom'; + /** + * @defaultValue false + */ + secondary?: boolean; + children: ReactNode; +} + +export type LinkItemType = + | MainItemType + | IconItemType + | ButtonItemType + | MenuItemType + | CustomItemType; + +/** + * Get Links Items with shortcuts + */ +export function getLinks( + links: LinkItemType[] = [], + githubUrl?: string, +): LinkItemType[] { + let result = links ?? []; + + if (githubUrl) + result = [ + ...result, + { + type: 'icon', + url: githubUrl, + text: 'Github', + label: 'GitHub', + icon: ( + + + + ), + external: true, + }, + ]; + + return result; +} + +export { BaseLinkItem } from './client'; diff --git a/src/components/root-toggle.tsx b/src/components/root-toggle.tsx new file mode 100644 index 00000000..02fed5df --- /dev/null +++ b/src/components/root-toggle.tsx @@ -0,0 +1,103 @@ +'use client'; +import { Check, ChevronsUpDown } from 'lucide-react'; +import { type ComponentProps, type ReactNode, useMemo, useState } from 'react'; +import Link from 'fumadocs-core/link'; +import { usePathname } from 'fumadocs-core/framework'; +import { cn } from '../lib/cn'; +import { isTabActive } from '../lib/is-active'; +import { useSidebar } from 'fumadocs-ui/contexts/sidebar'; +import { Popover, PopoverContent, PopoverTrigger } from './ui/popover'; +import type { SidebarTab } from 'fumadocs-ui/utils/get-sidebar-tabs'; + +export interface Option extends SidebarTab { + props?: ComponentProps<'a'>; +} + +export function RootToggle({ + options, + placeholder, + ...props +}: { + placeholder?: ReactNode; + options: Option[]; +} & ComponentProps<'button'>) { + const [open, setOpen] = useState(false); + const { closeOnRedirect } = useSidebar(); + const pathname = usePathname(); + + const selected = useMemo(() => { + return options.findLast((item) => isTabActive(item, pathname)); + }, [options, pathname]); + + const onClick = () => { + closeOnRedirect.current = false; + setOpen(false); + }; + + const item = selected ? ( + <> +
{selected.icon}
+
+

{selected.title}

+

+ {selected.description} +

+
+ + ) : ( + placeholder + ); + + return ( + + {item && ( + + {item} + + + )} + + {options.map((item) => { + const isActive = selected && item.url === selected.url; + if (!isActive && item.unlisted) return; + + return ( + +
+ {item.icon} +
+
+

{item.title}

+

+ {item.description} +

+
+ + + + ); + })} +
+
+ ); +} diff --git a/src/components/search-toggle.tsx b/src/components/search-toggle.tsx new file mode 100644 index 00000000..09529401 --- /dev/null +++ b/src/components/search-toggle.tsx @@ -0,0 +1,79 @@ +'use client'; +import type { ComponentProps } from 'react'; +import { Search } from 'lucide-react'; +import { useSearchContext } from 'fumadocs-ui/contexts/search'; +import { useI18n } from 'fumadocs-ui/contexts/i18n'; +import { cn } from '../lib/cn'; +import { type ButtonProps, buttonVariants } from './ui/button'; + +interface SearchToggleProps + extends Omit, 'color'>, + ButtonProps { + hideIfDisabled?: boolean; +} + +export function SearchToggle({ + hideIfDisabled, + size = 'icon-sm', + color = 'ghost', + ...props +}: SearchToggleProps) { + const { setOpenSearch, enabled } = useSearchContext(); + if (hideIfDisabled && !enabled) return null; + + return ( + + ); +} + +export function LargeSearchToggle({ + hideIfDisabled, + ...props +}: ComponentProps<'button'> & { + hideIfDisabled?: boolean; +}) { + const { enabled, hotKey, setOpenSearch } = useSearchContext(); + const { text } = useI18n(); + if (hideIfDisabled && !enabled) return null; + + return ( + + ); +} diff --git a/src/components/sidebar.tsx b/src/components/sidebar.tsx new file mode 100644 index 00000000..995debfb --- /dev/null +++ b/src/components/sidebar.tsx @@ -0,0 +1,559 @@ +'use client'; +import { ChevronDown, ExternalLink } from 'lucide-react'; +import { usePathname } from 'fumadocs-core/framework'; +import { + type ComponentProps, + createContext, + type FC, + Fragment, + type ReactNode, + useContext, + useMemo, + useRef, + useState, +} from 'react'; +import Link, { type LinkProps } from 'fumadocs-core/link'; +import { useOnChange } from 'fumadocs-core/utils/use-on-change'; +import { cn } from '../lib/cn'; +import { ScrollArea, ScrollViewport } from './ui/scroll-area'; +import { isActive } from '../lib/is-active'; +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from './ui/collapsible'; +import { type ScrollAreaProps } from '@radix-ui/react-scroll-area'; +import { useSidebar } from 'fumadocs-ui/contexts/sidebar'; +import { cva } from 'class-variance-authority'; +import type { + CollapsibleContentProps, + CollapsibleTriggerProps, +} from '@radix-ui/react-collapsible'; +import type { PageTree } from 'fumadocs-core/server'; +import { useTreeContext, useTreePath } from 'fumadocs-ui/contexts/tree'; +import { useMediaQuery } from 'fumadocs-core/utils/use-media-query'; +import { Presence } from '@radix-ui/react-presence'; + +export interface SidebarProps { + /** + * Open folders by default if their level is lower or equal to a specific level + * (Starting from 1) + * + * @defaultValue 0 + */ + defaultOpenLevel?: number; + + /** + * Prefetch links + * + * @defaultValue true + */ + prefetch?: boolean; + + /** + * Children to render + */ + Content: ReactNode; + + /** + * Alternative children for mobile + */ + Mobile?: ReactNode; +} + +interface InternalContext { + defaultOpenLevel: number; + prefetch: boolean; + level: number; +} + +const itemVariants = cva( + 'relative flex flex-row items-center gap-2 rounded-lg p-2 ps-(--sidebar-item-offset) text-start text-fd-muted-foreground [overflow-wrap:anywhere] [&_svg]:size-4 [&_svg]:shrink-0', + { + variants: { + active: { + true: 'bg-fd-primary/10 text-fd-primary', + false: + 'transition-colors hover:bg-fd-accent/50 hover:text-fd-accent-foreground/80 hover:transition-none', + }, + }, + }, +); + +const Context = createContext(null); +const FolderContext = createContext<{ + open: boolean; + setOpen: React.Dispatch>; +} | null>(null); + +export function Sidebar({ + defaultOpenLevel = 0, + prefetch = true, + Mobile, + Content, +}: SidebarProps) { + const isMobile = useMediaQuery('(width < 768px)') ?? false; + const context = useMemo(() => { + return { + defaultOpenLevel, + prefetch, + level: 1, + }; + }, [defaultOpenLevel, prefetch]); + + return ( + + {isMobile && Mobile != null ? Mobile : Content} + + ); +} + +export function SidebarContent(props: ComponentProps<'aside'>) { + const { collapsed } = useSidebar(); + const [hover, setHover] = useState(false); + const timerRef = useRef(0); + const closeTimeRef = useRef(0); + + useOnChange(collapsed, () => { + setHover(false); + closeTimeRef.current = Date.now() + 150; + }); + + return ( + + ); +} + +export function SidebarContentMobile({ + className, + children, + ...props +}: ComponentProps<'aside'>) { + const { open, setOpen } = useSidebar(); + const state = open ? 'open' : 'closed'; + + return ( + <> + +
setOpen(false)} + /> + + + {({ present }) => ( + + )} + + + ); +} + +export function SidebarHeader(props: ComponentProps<'div'>) { + return ( +
+ {props.children} +
+ ); +} + +export function SidebarFooter(props: ComponentProps<'div'>) { + return ( +
+ {props.children} +
+ ); +} + +export function SidebarViewport(props: ScrollAreaProps) { + return ( + + + {props.children} + + + ); +} + +export function SidebarSeparator(props: ComponentProps<'p'>) { + return ( +

+ {props.children} +

+ ); +} + +export function SidebarItem({ + icon, + ...props +}: LinkProps & { + icon?: ReactNode; +}) { + const pathname = usePathname(); + const active = + props.href !== undefined && isActive(props.href, pathname, false); + const { prefetch } = useInternalContext(); + + return ( + + {icon ?? (props.external ? : null)} + {props.children} + + ); +} + +export function SidebarFolder({ + defaultOpen = false, + ...props +}: ComponentProps<'div'> & { + defaultOpen?: boolean; +}) { + const [open, setOpen] = useState(defaultOpen); + + useOnChange(defaultOpen, (v) => { + if (v) setOpen(v); + }); + + return ( + + ({ open, setOpen }), [open])} + > + {props.children} + + + ); +} + +export function SidebarFolderTrigger({ + className, + ...props +}: CollapsibleTriggerProps) { + const { open } = useFolderContext(); + + return ( + + {props.children} + + + ); +} + +export function SidebarFolderLink(props: LinkProps) { + const { open, setOpen } = useFolderContext(); + const { prefetch } = useInternalContext(); + + const pathname = usePathname(); + const active = + props.href !== undefined && isActive(props.href, pathname, false); + + return ( + { + if ( + e.target instanceof Element && + e.target.matches('[data-icon], [data-icon] *') + ) { + setOpen(!open); + e.preventDefault(); + } else { + setOpen(active ? !open : true); + } + }} + prefetch={prefetch} + > + {props.children} + + + ); +} + +export function SidebarFolderContent(props: CollapsibleContentProps) { + const { level, ...ctx } = useInternalContext(); + + return ( + + ({ + ...ctx, + level: level + 1, + }), + [ctx, level], + )} + > + {props.children} + + + ); +} + +export function SidebarTrigger({ + children, + ...props +}: ComponentProps<'button'>) { + const { setOpen } = useSidebar(); + + return ( + + ); +} + +export function SidebarCollapseTrigger(props: ComponentProps<'button'>) { + const { collapsed, setCollapsed } = useSidebar(); + + return ( + + ); +} + +function useFolderContext() { + const ctx = useContext(FolderContext); + if (!ctx) throw new Error('Missing sidebar folder'); + + return ctx; +} + +function useInternalContext() { + const ctx = useContext(Context); + if (!ctx) throw new Error(' component required.'); + + return ctx; +} + +export interface SidebarComponents { + Item: FC<{ item: PageTree.Item }>; + Folder: FC<{ item: PageTree.Folder; level: number; children: ReactNode }>; + Separator: FC<{ item: PageTree.Separator }>; +} + +/** + * Render sidebar items from page tree + */ +export function SidebarPageTree(props: { + components?: Partial; +}) { + const { root } = useTreeContext(); + + return useMemo(() => { + const { Separator, Item, Folder } = props.components ?? {}; + + function renderSidebarList( + items: PageTree.Node[], + level: number, + ): ReactNode[] { + return items.map((item, i) => { + if (item.type === 'separator') { + if (Separator) return ; + return ( + + {item.icon} + {item.name} + + ); + } + + if (item.type === 'folder') { + const children = renderSidebarList(item.children, level + 1); + + if (Folder) + return ( + + {children} + + ); + return ( + + {children} + + ); + } + + if (Item) return ; + return ( + + {item.name} + + ); + }); + } + + return ( + {renderSidebarList(root.children, 1)} + ); + }, [props.components, root]); +} + +function PageTreeFolder({ + item, + ...props +}: { + item: PageTree.Folder; + children: ReactNode; +}) { + const { defaultOpenLevel, level } = useInternalContext(); + const path = useTreePath(); + + return ( + = level) || path.includes(item) + } + > + {item.index ? ( + + {item.icon} + {item.name} + + ) : ( + + {item.icon} + {item.name} + + )} + {props.children} + + ); +} diff --git a/src/components/theme-toggle.tsx b/src/components/theme-toggle.tsx new file mode 100644 index 00000000..4e6656cb --- /dev/null +++ b/src/components/theme-toggle.tsx @@ -0,0 +1,87 @@ +'use client'; +import { cva } from 'class-variance-authority'; +import { Moon, Sun, Airplay } from 'lucide-react'; +import { useTheme } from 'next-themes'; +import { type HTMLAttributes, useLayoutEffect, useState } from 'react'; +import { cn } from '../lib/cn'; + +const itemVariants = cva( + 'size-6.5 rounded-full p-1.5 text-fd-muted-foreground', + { + variants: { + active: { + true: 'bg-fd-accent text-fd-accent-foreground', + false: 'text-fd-muted-foreground', + }, + }, + }, +); + +const full = [ + ['light', Sun] as const, + ['dark', Moon] as const, + ['system', Airplay] as const, +]; + +export function ThemeToggle({ + className, + mode = 'light-dark', + ...props +}: HTMLAttributes & { + mode?: 'light-dark' | 'light-dark-system'; +}) { + const { setTheme, theme, resolvedTheme } = useTheme(); + const [mounted, setMounted] = useState(false); + + useLayoutEffect(() => { + setMounted(true); + }, []); + + const container = cn( + 'inline-flex items-center rounded-full border p-1', + className, + ); + + if (mode === 'light-dark') { + const value = mounted ? resolvedTheme : null; + + return ( + + ); + } + + const value = mounted ? theme : null; + + return ( +
+ {full.map(([key, Icon]) => ( + + ))} +
+ ); +} diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx index 7df62b94..5fca2adb 100644 --- a/src/components/ui/button.tsx +++ b/src/components/ui/button.tsx @@ -1,28 +1,28 @@ -import { cva, type VariantProps } from "class-variance-authority"; +import { cva, type VariantProps } from 'class-variance-authority'; const variants = { - primary: "bg-fd-primary text-fd-primary-foreground hover:bg-fd-primary/80", - outline: "border hover:bg-fd-accent hover:text-fd-accent-foreground", - ghost: "hover:bg-fd-accent hover:text-fd-accent-foreground", - secondary: - "border bg-fd-secondary text-fd-secondary-foreground hover:bg-fd-accent hover:text-fd-accent-foreground", + primary: 'bg-fd-primary text-fd-primary-foreground hover:bg-fd-primary/80', + outline: 'border hover:bg-fd-accent hover:text-fd-accent-foreground', + ghost: 'hover:bg-fd-accent hover:text-fd-accent-foreground', + secondary: + 'border bg-fd-secondary text-fd-secondary-foreground hover:bg-fd-accent hover:text-fd-accent-foreground', } as const; export const buttonVariants = cva( - "inline-flex items-center justify-center rounded-md p-2 text-sm font-medium transition-colors duration-100 disabled:pointer-events-none disabled:opacity-50 focus-visible:outline-none", - { - variants: { - variant: variants, - // fumadocs use `color` instead of `variant` - color: variants, - size: { - sm: "gap-1 px-2 py-1.5 text-xs", - icon: "p-1.5 [&_svg]:size-5", - "icon-sm": "p-1.5 [&_svg]:size-4.5", - "icon-xs": "p-1 [&_svg]:size-4", - }, - }, - }, + 'inline-flex items-center justify-center rounded-md p-2 text-sm font-medium transition-colors duration-100 disabled:pointer-events-none disabled:opacity-50 focus-visible:outline-none', + { + variants: { + variant: variants, + // fumadocs use `color` instead of `variant` + color: variants, + size: { + sm: 'gap-1 px-2 py-1.5 text-xs', + icon: 'p-1.5 [&_svg]:size-5', + 'icon-sm': 'p-1.5 [&_svg]:size-4.5', + 'icon-xs': 'p-1 [&_svg]:size-4', + }, + }, + }, ); export type ButtonProps = VariantProps; diff --git a/src/components/ui/collapsible.tsx b/src/components/ui/collapsible.tsx new file mode 100644 index 00000000..dbcf3f00 --- /dev/null +++ b/src/components/ui/collapsible.tsx @@ -0,0 +1,39 @@ +'use client'; +import * as CollapsiblePrimitive from '@radix-ui/react-collapsible'; +import { forwardRef, useEffect, useState } from 'react'; +import { cn } from '../../lib/cn'; + +const Collapsible = CollapsiblePrimitive.Root; + +const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger; + +const CollapsibleContent = forwardRef< + HTMLDivElement, + React.ComponentPropsWithoutRef +>(({ children, ...props }, ref) => { + const [mounted, setMounted] = useState(false); + + useEffect(() => { + setMounted(true); + }, []); + + return ( + + {children} + + ); +}); + +CollapsibleContent.displayName = + CollapsiblePrimitive.CollapsibleContent.displayName; + +export { Collapsible, CollapsibleTrigger, CollapsibleContent }; diff --git a/src/components/ui/popover.tsx b/src/components/ui/popover.tsx new file mode 100644 index 00000000..e033f5d7 --- /dev/null +++ b/src/components/ui/popover.tsx @@ -0,0 +1,32 @@ +'use client'; +import * as PopoverPrimitive from '@radix-ui/react-popover'; +import * as React from 'react'; +import { cn } from '../../lib/cn'; + +const Popover = PopoverPrimitive.Root; + +const PopoverTrigger = PopoverPrimitive.Trigger; + +const PopoverContent = React.forwardRef< + React.ComponentRef, + React.ComponentPropsWithoutRef +>(({ className, align = 'center', sideOffset = 4, ...props }, ref) => ( + + + +)); +PopoverContent.displayName = PopoverPrimitive.Content.displayName; + +const PopoverClose = PopoverPrimitive.PopoverClose; + +export { Popover, PopoverTrigger, PopoverContent, PopoverClose }; diff --git a/src/components/ui/scroll-area.tsx b/src/components/ui/scroll-area.tsx new file mode 100644 index 00000000..86cb150e --- /dev/null +++ b/src/components/ui/scroll-area.tsx @@ -0,0 +1,58 @@ +import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area'; +import * as React from 'react'; +import { cn } from '../../lib/cn'; + +const ScrollArea = React.forwardRef< + React.ComponentRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + {children} + + + +)); + +ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName; + +const ScrollViewport = React.forwardRef< + React.ComponentRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + {children} + +)); + +ScrollViewport.displayName = ScrollAreaPrimitive.Viewport.displayName; + +const ScrollBar = React.forwardRef< + React.ComponentRef, + React.ComponentPropsWithoutRef +>(({ className, orientation = 'vertical', ...props }, ref) => ( + + + +)); +ScrollBar.displayName = ScrollAreaPrimitive.Scrollbar.displayName; + +export { ScrollArea, ScrollBar, ScrollViewport }; diff --git a/src/components/ui/toc-clerk.tsx b/src/components/ui/toc-clerk.tsx new file mode 100644 index 00000000..f8466ab4 --- /dev/null +++ b/src/components/ui/toc-clerk.tsx @@ -0,0 +1,179 @@ +'use client'; +import type { TOCItemType } from 'fumadocs-core/server'; +import * as Primitive from 'fumadocs-core/toc'; +import { type ComponentProps, useEffect, useRef, useState } from 'react'; +import { cn } from '../../lib/cn'; +import { TocThumb } from './toc-thumb'; +import { useTOCItems } from './toc'; +import { mergeRefs } from '../../lib/merge-refs'; +import { useI18n } from 'fumadocs-ui/contexts/i18n'; + +export default function ClerkTOCItems({ + ref, + className, + ...props +}: ComponentProps<'div'>) { + const containerRef = useRef(null); + const items = useTOCItems(); + const { text } = useI18n(); + + const [svg, setSvg] = useState<{ + path: string; + width: number; + height: number; + }>(); + + useEffect(() => { + if (!containerRef.current) return; + const container = containerRef.current; + + function onResize(): void { + if (container.clientHeight === 0) return; + let w = 0, + h = 0; + const d: string[] = []; + for (let i = 0; i < items.length; i++) { + const element: HTMLElement | null = container.querySelector( + `a[href="#${items[i].url.slice(1)}"]`, + ); + if (!element) continue; + + const styles = getComputedStyle(element); + const offset = getLineOffset(items[i].depth) + 1, + top = element.offsetTop + parseFloat(styles.paddingTop), + bottom = + element.offsetTop + + element.clientHeight - + parseFloat(styles.paddingBottom); + + w = Math.max(offset, w); + h = Math.max(h, bottom); + + d.push(`${i === 0 ? 'M' : 'L'}${offset} ${top}`); + d.push(`L${offset} ${bottom}`); + } + + setSvg({ + path: d.join(' '), + width: w + 1, + height: h, + }); + } + + const observer = new ResizeObserver(onResize); + onResize(); + + observer.observe(container); + return () => { + observer.disconnect(); + }; + }, [items]); + + if (items.length === 0) + return ( +
+ {text.tocNoHeadings} +
+ ); + + return ( + <> + {svg ? ( +
`, + ) + }")`, + }} + > + +
+ ) : null} +
+ {items.map((item, i) => ( + + ))} +
+ + ); +} + +function getItemOffset(depth: number): number { + if (depth <= 2) return 14; + if (depth === 3) return 26; + return 36; +} + +function getLineOffset(depth: number): number { + return depth >= 3 ? 10 : 0; +} + +function TOCItem({ + item, + upper = item.depth, + lower = item.depth, +}: { + item: TOCItemType; + upper?: number; + lower?: number; +}) { + const offset = getLineOffset(item.depth), + upperOffset = getLineOffset(upper), + lowerOffset = getLineOffset(lower); + + return ( + + {offset !== upperOffset ? ( + + + + ) : null} +
+ {item.title} + + ); +} diff --git a/src/components/ui/toc-thumb.tsx b/src/components/ui/toc-thumb.tsx new file mode 100644 index 00000000..bdced31a --- /dev/null +++ b/src/components/ui/toc-thumb.tsx @@ -0,0 +1,73 @@ +import { type HTMLAttributes, type RefObject, useEffect, useRef } from 'react'; +import * as Primitive from 'fumadocs-core/toc'; +import { useOnChange } from 'fumadocs-core/utils/use-on-change'; +import { useEffectEvent } from 'fumadocs-core/utils/use-effect-event'; + +export type TOCThumb = [top: number, height: number]; + +function calc(container: HTMLElement, active: string[]): TOCThumb { + if (active.length === 0 || container.clientHeight === 0) { + return [0, 0]; + } + + let upper = Number.MAX_VALUE, + lower = 0; + + for (const item of active) { + const element = container.querySelector(`a[href="#${item}"]`); + if (!element) continue; + + const styles = getComputedStyle(element); + upper = Math.min(upper, element.offsetTop + parseFloat(styles.paddingTop)); + lower = Math.max( + lower, + element.offsetTop + + element.clientHeight - + parseFloat(styles.paddingBottom), + ); + } + + return [upper, lower - upper]; +} + +function update(element: HTMLElement, info: TOCThumb): void { + element.style.setProperty('--fd-top', `${info[0]}px`); + element.style.setProperty('--fd-height', `${info[1]}px`); +} + +export function TocThumb({ + containerRef, + ...props +}: HTMLAttributes & { + containerRef: RefObject; +}) { + const active = Primitive.useActiveAnchors(); + const thumbRef = useRef(null); + + const onResize = useEffectEvent(() => { + if (!containerRef.current || !thumbRef.current) return; + + update(thumbRef.current, calc(containerRef.current, active)); + }); + + useEffect(() => { + if (!containerRef.current) return; + const container = containerRef.current; + + onResize(); + const observer = new ResizeObserver(onResize); + observer.observe(container); + + return () => { + observer.disconnect(); + }; + }, [containerRef, onResize]); + + useOnChange(active, () => { + if (!containerRef.current || !thumbRef.current) return; + + update(thumbRef.current, calc(containerRef.current, active)); + }); + + return
; +} diff --git a/src/components/ui/toc.tsx b/src/components/ui/toc.tsx new file mode 100644 index 00000000..81cebc2d --- /dev/null +++ b/src/components/ui/toc.tsx @@ -0,0 +1,101 @@ +'use client'; +import type { TOCItemType } from 'fumadocs-core/server'; +import * as Primitive from 'fumadocs-core/toc'; +import { type ComponentProps, createContext, useContext, useRef } from 'react'; +import { cn } from '../../lib/cn'; +import { useI18n } from 'fumadocs-ui/contexts/i18n'; +import { TocThumb } from './toc-thumb'; +import { mergeRefs } from '../../lib/merge-refs'; + +const TOCContext = createContext([]); + +export function useTOCItems(): TOCItemType[] { + return useContext(TOCContext); +} + +export function TOCProvider({ + toc, + children, + ...props +}: ComponentProps) { + return ( + + + {children} + + + ); +} + +export function TOCScrollArea({ + ref, + className, + ...props +}: ComponentProps<'div'>) { + const viewRef = useRef(null); + + return ( +
+ + {props.children} + +
+ ); +} + +export function TOCItems({ ref, className, ...props }: ComponentProps<'div'>) { + const containerRef = useRef(null); + const items = useTOCItems(); + const { text } = useI18n(); + + if (items.length === 0) + return ( +
+ {text.tocNoHeadings} +
+ ); + + return ( + <> + +
+ {items.map((item) => ( + + ))} +
+ + ); +} + +function TOCItem({ item }: { item: TOCItemType }) { + return ( + = 4 && 'ps-8', + )} + > + {item.title} + + ); +} diff --git a/src/lib/cn.ts b/src/lib/cn.ts index 8e473dac..ba66fd25 100644 --- a/src/lib/cn.ts +++ b/src/lib/cn.ts @@ -1 +1 @@ -export { twMerge as cn } from "tailwind-merge"; +export { twMerge as cn } from 'tailwind-merge'; diff --git a/src/lib/is-active.ts b/src/lib/is-active.ts new file mode 100644 index 00000000..d41bf698 --- /dev/null +++ b/src/lib/is-active.ts @@ -0,0 +1,23 @@ +import type { SidebarTab } from 'fumadocs-ui/utils/get-sidebar-tabs'; + +function normalize(url: string) { + if (url.length > 1 && url.endsWith('/')) return url.slice(0, -1); + return url; +} + +export function isActive( + url: string, + pathname: string, + nested = true, +): boolean { + url = normalize(url); + pathname = normalize(pathname); + + return url === pathname || (nested && pathname.startsWith(`${url}/`)); +} + +export function isTabActive(tab: SidebarTab, pathname: string) { + if (tab.urls) return tab.urls.has(normalize(pathname)); + + return isActive(tab.url, pathname, true); +} diff --git a/src/lib/merge-refs.ts b/src/lib/merge-refs.ts new file mode 100644 index 00000000..7d05f74a --- /dev/null +++ b/src/lib/merge-refs.ts @@ -0,0 +1,15 @@ +import type * as React from 'react'; + +export function mergeRefs( + ...refs: (React.Ref | undefined)[] +): React.RefCallback { + return (value) => { + refs.forEach((ref) => { + if (typeof ref === 'function') { + ref(value); + } else if (ref) { + ref.current = value; + } + }); + }; +} From 90d920382547ae261104d986dd00852a21afa6d1 Mon Sep 17 00:00:00 2001 From: Steve Date: Tue, 9 Sep 2025 13:25:21 -0400 Subject: [PATCH 10/14] chore: lint and format --- src/app/(docs)/[...slug]/layout.tsx | 2 +- src/app/layout.config.tsx | 2 +- src/components/language-toggle.tsx | 110 +- src/components/layout/docs/client.tsx | 232 ++--- src/components/layout/docs/index.tsx | 1083 ++++++++++---------- src/components/layout/docs/page-client.tsx | 741 +++++++------- src/components/layout/docs/page.tsx | 167 +-- src/components/layout/page.tsx | 466 ++++----- src/components/layout/shared/client.tsx | 52 +- src/components/layout/shared/index.tsx | 289 +++--- src/components/root-toggle.tsx | 180 ++-- src/components/search-toggle.tsx | 135 +-- src/components/sidebar.tsx | 972 +++++++++--------- src/components/theme-toggle.tsx | 139 +-- src/components/ui/button.tsx | 40 +- src/components/ui/collapsible.tsx | 50 +- src/components/ui/popover.tsx | 40 +- src/components/ui/scroll-area.tsx | 80 +- src/components/ui/toc-clerk.tsx | 332 +++--- src/components/ui/toc-thumb.tsx | 104 +- src/components/ui/toc.tsx | 162 +-- src/lib/cn.ts | 2 +- src/lib/is-active.ts | 22 +- src/lib/merge-refs.ts | 22 +- 24 files changed, 2726 insertions(+), 2698 deletions(-) diff --git a/src/app/(docs)/[...slug]/layout.tsx b/src/app/(docs)/[...slug]/layout.tsx index a0815b85..5608cd83 100644 --- a/src/app/(docs)/[...slug]/layout.tsx +++ b/src/app/(docs)/[...slug]/layout.tsx @@ -58,7 +58,7 @@ export default async function Layout({ return ( ; export function LanguageToggle(props: LanguageSelectProps): React.ReactElement { - const context = useI18n(); - if (!context.locales) throw new Error('Missing ``'); + const context = useI18n(); + if (!context.locales) throw new Error("Missing ``"); - return ( - - - {props.children} - - -

- {context.text.chooseLanguage} -

- {context.locales.map((item) => ( - - ))} -
-
- ); + return ( + + + {props.children} + + +

+ {context.text.chooseLanguage} +

+ {context.locales.map((item) => ( + + ))} +
+
+ ); } export function LanguageToggleText( - props: HTMLAttributes, + props: HTMLAttributes, ): React.ReactElement { - const context = useI18n(); - const text = context.locales?.find( - (item) => item.locale === context.locale, - )?.name; + const context = useI18n(); + const text = context.locales?.find( + (item) => item.locale === context.locale, + )?.name; - return {text}; + return {text}; } diff --git a/src/components/layout/docs/client.tsx b/src/components/layout/docs/client.tsx index fd6b25a9..686c9af6 100644 --- a/src/components/layout/docs/client.tsx +++ b/src/components/layout/docs/client.tsx @@ -1,136 +1,138 @@ -'use client'; -import { cn } from '../../../lib/cn'; -import { type ComponentProps, useMemo } from 'react'; -import { useSidebar } from 'fumadocs-ui/contexts/sidebar'; -import { useNav } from 'fumadocs-ui/contexts/layout'; -import { buttonVariants } from '../../ui/button'; -import { Sidebar as SidebarIcon } from 'lucide-react'; -import Link from 'fumadocs-core/link'; -import { usePathname } from 'fumadocs-core/framework'; -import { isTabActive } from '../../../lib/is-active'; -import type { Option } from '../../root-toggle'; +"use client"; +import { usePathname } from "fumadocs-core/framework"; +import Link from "fumadocs-core/link"; +import { useNav } from "fumadocs-ui/contexts/layout"; +import { useSidebar } from "fumadocs-ui/contexts/sidebar"; +import { Sidebar as SidebarIcon } from "lucide-react"; +import { type ComponentProps, useId, useMemo } from "react"; +import { cn } from "../../../lib/cn"; +import { isTabActive } from "../../../lib/is-active"; +import type { Option } from "../../root-toggle"; +import { buttonVariants } from "../../ui/button"; export function Navbar({ - mode, - ...props -}: ComponentProps<'header'> & { mode: 'top' | 'auto' }) { - const { open, collapsed } = useSidebar(); - const { isTransparent } = useNav(); + mode, + ...props +}: ComponentProps<"header"> & { mode: "top" | "auto" }) { + const { open, collapsed } = useSidebar(); + const { isTransparent } = useNav(); + const subnavId = useId(); - return ( -
- {props.children} -
- ); + return ( +
+ {props.children} +
+ ); } -export function LayoutBody(props: ComponentProps<'main'>) { - const { collapsed } = useSidebar(); +export function LayoutBody(props: ComponentProps<"main">) { + const { collapsed } = useSidebar(); + const layoutId = useId(); - return ( -
- {props.children} -
- ); + return ( +
+ {props.children} +
+ ); } export function NavbarSidebarTrigger({ - className, - ...props -}: ComponentProps<'button'>) { - const { setOpen } = useSidebar(); + className, + ...props +}: ComponentProps<"button">) { + const { setOpen } = useSidebar(); - return ( - - ); + return ( + + ); } export function LayoutTabs({ - options, - ...props -}: ComponentProps<'div'> & { - options: Option[]; + options, + ...props +}: ComponentProps<"div"> & { + options: Option[]; }) { - const pathname = usePathname(); - const selected = useMemo(() => { - return options.findLast((option) => isTabActive(option, pathname)); - }, [options, pathname]); + const pathname = usePathname(); + const selected = useMemo(() => { + return options.findLast((option) => isTabActive(option, pathname)); + }, [options, pathname]); - return ( -
- {options.map((option) => ( - - ))} -
- ); + return ( +
+ {options.map((option) => ( + + ))} +
+ ); } function LayoutTab({ - option: { title, url, unlisted, props }, - selected = false, + option: { title, url, unlisted, props }, + selected = false, }: { - option: Option; - selected?: boolean; + option: Option; + selected?: boolean; }) { - return ( - - {title} - - ); + return ( + + {title} + + ); } diff --git a/src/components/layout/docs/index.tsx b/src/components/layout/docs/index.tsx index 6a13c0f9..5badbce4 100644 --- a/src/components/layout/docs/index.tsx +++ b/src/components/layout/docs/index.tsx @@ -1,574 +1,575 @@ +import { HideIfEmpty } from "fumadocs-core/hide-if-empty"; +import Link from "fumadocs-core/link"; +import type { PageTree } from "fumadocs-core/server"; +import { NavProvider } from "fumadocs-ui/contexts/layout"; +import { TreeContextProvider } from "fumadocs-ui/contexts/tree"; import { - type ComponentProps, - Fragment, - type HTMLAttributes, - type ReactNode, - useMemo, -} from 'react'; + type GetSidebarTabsOptions, + getSidebarTabs, +} from "fumadocs-ui/utils/get-sidebar-tabs"; import { - type BaseLayoutProps, - BaseLinkItem, - type BaseLinkType, - getLinks, - type LinkItemType, -} from '../shared/index'; + ChevronDown, + Languages, + Sidebar as SidebarIcon, + X, +} from "lucide-react"; import { - Sidebar, - SidebarCollapseTrigger, - type SidebarComponents, - SidebarContent, - SidebarContentMobile, - SidebarFolder, - SidebarFolderContent, - SidebarFolderLink, - SidebarFolderTrigger, - SidebarFooter, - SidebarHeader, - SidebarItem, - SidebarPageTree, - type SidebarProps, - SidebarTrigger, - SidebarViewport, -} from '../../sidebar'; -import { TreeContextProvider } from 'fumadocs-ui/contexts/tree'; -import { cn } from '../../../lib/cn'; -import { buttonVariants } from '../../ui/button'; + type ComponentProps, + Fragment, + type HTMLAttributes, + type ReactNode, + useMemo, +} from "react"; +import { cn } from "../../../lib/cn"; +import { LanguageToggle } from "../../language-toggle"; +import { type Option, RootToggle } from "../../root-toggle"; +import { LargeSearchToggle, SearchToggle } from "../../search-toggle"; import { - ChevronDown, - Languages, - Sidebar as SidebarIcon, - X, -} from 'lucide-react'; -import { LanguageToggle } from '../../language-toggle'; -import { ThemeToggle } from '../../theme-toggle'; + Sidebar, + SidebarCollapseTrigger, + type SidebarComponents, + SidebarContent, + SidebarContentMobile, + SidebarFolder, + SidebarFolderContent, + SidebarFolderLink, + SidebarFolderTrigger, + SidebarFooter, + SidebarHeader, + SidebarItem, + SidebarPageTree, + type SidebarProps, + SidebarTrigger, + SidebarViewport, +} from "../../sidebar"; +import { ThemeToggle } from "../../theme-toggle"; +import { buttonVariants } from "../../ui/button"; +import { Popover, PopoverContent, PopoverTrigger } from "../../ui/popover"; import { - Popover, - PopoverContent, - PopoverTrigger, -} from '../../ui/popover'; -import type { PageTree } from 'fumadocs-core/server'; -import { - LayoutBody, - LayoutTabs, - Navbar, - NavbarSidebarTrigger, -} from './client'; -import { NavProvider } from 'fumadocs-ui/contexts/layout'; -import { type Option, RootToggle } from '../../root-toggle'; -import Link from 'fumadocs-core/link'; -import { - LargeSearchToggle, - SearchToggle, -} from '../../search-toggle'; -import { HideIfEmpty } from 'fumadocs-core/hide-if-empty'; -import { - getSidebarTabs, - type GetSidebarTabsOptions, -} from 'fumadocs-ui/utils/get-sidebar-tabs'; + type BaseLayoutProps, + BaseLinkItem, + type BaseLinkType, + getLinks, + type LinkItemType, +} from "../shared/index"; +import { LayoutBody, LayoutTabs, Navbar, NavbarSidebarTrigger } from "./client"; export interface DocsLayoutProps extends BaseLayoutProps { - tree: PageTree.Root; - tabMode?: 'sidebar' | 'navbar'; + tree: PageTree.Root; + tabMode?: "sidebar" | "navbar"; - nav?: BaseLayoutProps['nav'] & { - mode?: 'top' | 'auto'; - }; + nav?: BaseLayoutProps["nav"] & { + mode?: "top" | "auto"; + }; - sidebar?: SidebarOptions; + sidebar?: SidebarOptions; - containerProps?: HTMLAttributes; + containerProps?: HTMLAttributes; } interface SidebarOptions - extends ComponentProps<'aside'>, - Pick { - components?: Partial; - - /** - * Root Toggle options - */ - tabs?: Option[] | GetSidebarTabsOptions | false; - - banner?: ReactNode; - footer?: ReactNode; - - /** - * Support collapsing the sidebar on desktop mode - * - * @defaultValue true - */ - collapsible?: boolean; + extends ComponentProps<"aside">, + Pick { + components?: Partial; + + /** + * Root Toggle options + */ + tabs?: Option[] | GetSidebarTabsOptions | false; + + banner?: ReactNode; + footer?: ReactNode; + + /** + * Support collapsing the sidebar on desktop mode + * + * @defaultValue true + */ + collapsible?: boolean; } export function DocsLayout(props: DocsLayoutProps) { - const { - tabMode = 'sidebar', - nav: { transparentMode, ...nav } = {}, - sidebar: { tabs: tabOptions, ...sidebarProps } = {}, - i18n = false, - disableThemeSwitch = false, - themeSwitch = { enabled: !disableThemeSwitch }, - } = props; - - const navMode = nav.mode ?? 'auto'; - const links = getLinks(props.links ?? [], props.githubUrl); - const tabs = useMemo(() => { - if (Array.isArray(tabOptions)) { - return tabOptions; - } - - if (typeof tabOptions === 'object') { - return getSidebarTabs(props.tree, tabOptions); - } - - if (tabOptions !== false) { - return getSidebarTabs(props.tree); - } - - return []; - }, [tabOptions, props.tree]); - - function sidebar() { - const { - banner, - footer, - components, - collapsible = true, - prefetch, - defaultOpenLevel, - ...rest - } = sidebarProps; - const iconLinks = links.filter((item) => item.type === 'icon'); - - const rootToggle = ( - <> - {tabMode === 'sidebar' && tabs.length > 0 && ( - - )} - {tabMode === 'navbar' && tabs.length > 0 && ( - - )} - - ); - - const sidebarNav = ( -
- - {nav.title} - - {collapsible && ( - - - - )} -
- ); - - const viewport = ( - - {links - .filter((item) => item.type !== 'icon') - .map((item, i, arr) => ( - - ))} - - - - ); - - const content = ( - - - {navMode === 'auto' && sidebarNav} - {nav.children} - {banner} - {rootToggle} - - {viewport} - - {iconLinks.map((item, i) => ( - - {item.icon} - - ))} - {footer} - - - ); - - const mobile = ( - - - - - - {banner} - {rootToggle} - - {viewport} - - {iconLinks.map((item, i) => ( - - {item.icon} - - ))} - {i18n ? ( - - - - ) : null} - {themeSwitch.enabled !== false && - (themeSwitch.component ?? ( - - ))} - {footer} - - - ); - - return ( - - ); - } - - return ( - - - - {sidebar()} - - {props.children} - - - - ); + const { + tabMode = "sidebar", + nav: { transparentMode, ...nav } = {}, + sidebar: { tabs: tabOptions, ...sidebarProps } = {}, + i18n = false, + disableThemeSwitch = false, + themeSwitch = { enabled: !disableThemeSwitch }, + } = props; + + const navMode = nav.mode ?? "auto"; + const links = getLinks(props.links ?? [], props.githubUrl); + const tabs = useMemo(() => { + if (Array.isArray(tabOptions)) { + return tabOptions; + } + + if (typeof tabOptions === "object") { + return getSidebarTabs(props.tree, tabOptions); + } + + if (tabOptions !== false) { + return getSidebarTabs(props.tree); + } + + return []; + }, [tabOptions, props.tree]); + + function sidebar() { + const { + banner, + footer, + components, + collapsible = true, + prefetch, + defaultOpenLevel, + ...rest + } = sidebarProps; + const iconLinks = links.filter((item) => item.type === "icon"); + + const rootToggle = ( + <> + {tabMode === "sidebar" && tabs.length > 0 && ( + + )} + {tabMode === "navbar" && tabs.length > 0 && ( + + )} + + ); + + const sidebarNav = ( +
+ + {nav.title} + + {collapsible && ( + + + + )} +
+ ); + + const viewport = ( + + {links + .filter((item) => item.type !== "icon") + .map((item, i, arr) => ( + + ))} + + + + ); + + const content = ( + + + {navMode === "auto" && sidebarNav} + {nav.children} + {banner} + {rootToggle} + + {viewport} + + {iconLinks.map((item, i) => ( + + {item.icon} + + ))} + {footer} + + + ); + + const mobile = ( + + + + + + {banner} + {rootToggle} + + {viewport} + + {iconLinks.map((item, i) => ( + + {item.icon} + + ))} + {i18n ? ( + + + + ) : null} + {themeSwitch.enabled !== false && + (themeSwitch.component ?? ( + + ))} + {footer} + + + ); + + return ( + + ); + } + + return ( + + + + {sidebar()} + + {props.children} + + + + ); } function DocsNavbar({ - links, - tabs, - searchToggle = {}, - themeSwitch = {}, - nav = {}, - ...props + links, + tabs, + searchToggle = {}, + themeSwitch = {}, + nav = {}, + ...props }: DocsLayoutProps & { - links: LinkItemType[]; - tabs: Option[]; + links: LinkItemType[]; + tabs: Option[]; }) { - const navMode = nav.mode ?? 'auto'; - const sidebarCollapsible = props.sidebar?.collapsible ?? true; - - return ( - 0 && 'lg:on-root:[--fd-nav-height:104px]', - )} - > -
-
- {sidebarCollapsible && navMode === 'auto' && ( - - - - )} - - {nav.title} - -
- {searchToggle.enabled !== false && - (searchToggle.components?.lg ? ( -
- {searchToggle.components.lg} -
- ) : ( - - ))} -
-
- {links - .filter((item) => item.type !== 'icon') - .map((item, i) => ( - - ))} -
- {nav.children} - {links - .filter((item) => item.type === 'icon') - .map((item, i) => ( - - {item.icon} - - ))} - -
- {searchToggle.enabled !== false && - (searchToggle.components?.sm ?? ( - - ))} - -
- -
- {props.i18n ? ( - - - - ) : null} - {themeSwitch.enabled !== false && - (themeSwitch.component ?? ( - - ))} - {sidebarCollapsible && navMode === 'top' && ( - - - - )} -
-
-
- {tabs.length > 0 && ( - - )} -
- ); + const navMode = nav.mode ?? "auto"; + const sidebarCollapsible = props.sidebar?.collapsible ?? true; + + return ( + 0 && "lg:on-root:[--fd-nav-height:104px]", + )} + > +
+
+ {sidebarCollapsible && navMode === "auto" && ( + + + + )} + + {nav.title} + +
+ {searchToggle.enabled !== false && + (searchToggle.components?.lg ? ( +
+ {searchToggle.components.lg} +
+ ) : ( + + ))} +
+
+ {links + .filter((item) => item.type !== "icon") + .map((item, i) => ( + + ))} +
+ {nav.children} + {links + .filter((item) => item.type === "icon") + .map((item, i) => ( + + {item.icon} + + ))} + +
+ {searchToggle.enabled !== false && + (searchToggle.components?.sm ?? ( + + ))} + +
+ +
+ {props.i18n ? ( + + + + ) : null} + {themeSwitch.enabled !== false && + (themeSwitch.component ?? ( + + ))} + {sidebarCollapsible && navMode === "top" && ( + + + + )} +
+
+
+ {tabs.length > 0 && ( + + )} +
+ ); } function NavbarLinkItem({ - item, - ...props + item, + ...props }: { item: LinkItemType } & HTMLAttributes) { - if (item.type === 'menu') { - return ( - - - {item.url ? ( - {item.text} - ) : ( - item.text - )} - - - - {item.items.map((child, i) => { - if (child.type === 'custom') - return {child.children}; - - return ( - - {child.icon} - {child.text} - - ); - })} - - - ); - } - - if (item.type === 'custom') return item.children; - - return ( - - {item.text} - - ); + if (item.type === "menu") { + return ( + + + {item.url ? ( + {item.text} + ) : ( + item.text + )} + + + + {item.items.map((child, _i) => { + // Create a stable key using item properties, avoiding array index + const key = String( + ("url" in child && child.url) || + ("text" in child && child.text) || + `menu-item-${child.type}`, + ); + if (child.type === "custom") + return ( + {child.children} + ); + + return ( + + {child.icon} + {child.text} + + ); + })} + + + ); + } + + if (item.type === "custom") return item.children; + + return ( + + {item.text} + + ); } function SidebarLinkItem({ - item, - ...props + item, + ...props }: { - item: Exclude; - className?: string; + item: Exclude; + className?: string; }) { - if (item.type === 'menu') - return ( - - {item.url ? ( - - {item.icon} - {item.text} - - ) : ( - - {item.icon} - {item.text} - - )} - - {item.items.map((child, i) => ( - - ))} - - - ); - - if (item.type === 'custom') return
{item.children}
; - - return ( - - {item.text} - - ); + if (item.type === "menu") + return ( + + {item.url ? ( + + {item.icon} + {item.text} + + ) : ( + + {item.icon} + {item.text} + + )} + + {item.items.map((child) => { + const key = String( + ("url" in child && child.url) || + ("text" in child && child.text) || + `sidebar-item-${child.type}`, + ); + return ; + })} + + + ); + + if (item.type === "custom") return
{item.children}
; + + return ( + + {item.text} + + ); } export { Navbar, NavbarSidebarTrigger }; diff --git a/src/components/layout/docs/page-client.tsx b/src/components/layout/docs/page-client.tsx index e84846b4..3b40a58d 100644 --- a/src/components/layout/docs/page-client.tsx +++ b/src/components/layout/docs/page-client.tsx @@ -1,417 +1,420 @@ -'use client'; +"use client"; import { - type ComponentProps, - Fragment, - useEffect, - useMemo, - useRef, - useState, -} from 'react'; -import { ChevronDown, ChevronLeft, ChevronRight } from 'lucide-react'; -import Link from 'fumadocs-core/link'; -import { cn } from '../../../lib/cn'; -import { useI18n } from 'fumadocs-ui/contexts/i18n'; -import { useTreeContext, useTreePath } from 'fumadocs-ui/contexts/tree'; -import type { PageTree } from 'fumadocs-core/server'; -import { createContext, usePathname } from 'fumadocs-core/framework'; + type BreadcrumbOptions, + getBreadcrumbItemsFromPath, +} from "fumadocs-core/breadcrumb"; +import { createContext, usePathname } from "fumadocs-core/framework"; +import Link from "fumadocs-core/link"; +import type { PageTree } from "fumadocs-core/server"; +import { useActiveAnchor } from "fumadocs-core/toc"; +import { useEffectEvent } from "fumadocs-core/utils/use-effect-event"; +import { useI18n } from "fumadocs-ui/contexts/i18n"; +import { useNav } from "fumadocs-ui/contexts/layout"; +import { useSidebar } from "fumadocs-ui/contexts/sidebar"; +import { useTreeContext, useTreePath } from "fumadocs-ui/contexts/tree"; +import { ChevronDown, ChevronLeft, ChevronRight } from "lucide-react"; import { - type BreadcrumbOptions, - getBreadcrumbItemsFromPath, -} from 'fumadocs-core/breadcrumb'; -import { useNav } from 'fumadocs-ui/contexts/layout'; -import { isActive } from '../../../lib/is-active'; -import { useEffectEvent } from 'fumadocs-core/utils/use-effect-event'; + type ComponentProps, + Fragment, + useEffect, + useId, + useMemo, + useRef, + useState, +} from "react"; +import { cn } from "../../../lib/cn"; +import { isActive } from "../../../lib/is-active"; import { - Collapsible, - CollapsibleContent, - CollapsibleTrigger, -} from '../../ui/collapsible'; -import { useSidebar } from 'fumadocs-ui/contexts/sidebar'; -import { useTOCItems } from '../../ui/toc'; -import { useActiveAnchor } from 'fumadocs-core/toc'; + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from "../../ui/collapsible"; +import { useTOCItems } from "../../ui/toc"; const TocPopoverContext = createContext<{ - open: boolean; - setOpen: (open: boolean) => void; -}>('TocPopoverContext'); - -export function PageTOCPopoverTrigger(props: ComponentProps<'button'>) { - const { text } = useI18n(); - const { open } = TocPopoverContext.use(); - const items = useTOCItems(); - const active = useActiveAnchor(); - const selected = useMemo( - () => items.findIndex((item) => active === item.url.slice(1)), - [items, active], - ); - const path = useTreePath().at(-1); - const showItem = selected !== -1 && !open; - - return ( - - - - - {path?.name ?? text.toc} - - - {items[selected]?.title} - - - - - ); + open: boolean; + setOpen: (open: boolean) => void; +}>("TocPopoverContext"); + +export function PageTOCPopoverTrigger(props: ComponentProps<"button">) { + const { text } = useI18n(); + const { open } = TocPopoverContext.use(); + const items = useTOCItems(); + const active = useActiveAnchor(); + const selected = useMemo( + () => items.findIndex((item) => active === item.url.slice(1)), + [items, active], + ); + const path = useTreePath().at(-1); + const showItem = selected !== -1 && !open; + + return ( + + + + + {path?.name ?? text.toc} + + + {items[selected]?.title} + + + + + ); } interface ProgressCircleProps - extends Omit, 'strokeWidth'> { - value: number; - strokeWidth?: number; - size?: number; - min?: number; - max?: number; + extends Omit, "strokeWidth"> { + value: number; + strokeWidth?: number; + size?: number; + min?: number; + max?: number; } function clamp(input: number, min: number, max: number): number { - if (input < min) return min; - if (input > max) return max; - return input; + if (input < min) return min; + if (input > max) return max; + return input; } function ProgressCircle({ - value, - strokeWidth = 2, - size = 24, - min = 0, - max = 100, - ...restSvgProps + value, + strokeWidth = 2, + size = 24, + min = 0, + max = 100, + ...restSvgProps }: ProgressCircleProps) { - const normalizedValue = clamp(value, min, max); - const radius = (size - strokeWidth) / 2; - const circumference = 2 * Math.PI * radius; - const progress = (normalizedValue / max) * circumference; - const circleProps = { - cx: size / 2, - cy: size / 2, - r: radius, - fill: 'none', - strokeWidth, - }; - - return ( - - - - - ); + const normalizedValue = clamp(value, min, max); + const radius = (size - strokeWidth) / 2; + const circumference = 2 * Math.PI * radius; + const progress = (normalizedValue / max) * circumference; + const circleProps = { + cx: size / 2, + cy: size / 2, + r: radius, + fill: "none", + strokeWidth, + }; + + return ( + + + + + ); } -export function PageTOCPopoverContent(props: ComponentProps<'div'>) { - return ( - - {props.children} - - ); +export function PageTOCPopoverContent(props: ComponentProps<"div">) { + return ( + + {props.children} + + ); } -export function PageTOCPopover(props: ComponentProps<'div'>) { - const ref = useRef(null); - const [open, setOpen] = useState(false); - const { collapsed } = useSidebar(); - const { isTransparent } = useNav(); - - const onClick = useEffectEvent((e: Event) => { - if (!open) return; - - if (ref.current && !ref.current.contains(e.target as HTMLElement)) - setOpen(false); - }); - - useEffect(() => { - window.addEventListener('click', onClick); - - return () => { - window.removeEventListener('click', onClick); - }; - }, [onClick]); - - return ( - ({ - open, - setOpen, - }), - [setOpen, open], - )} - > - -
- {props.children} -
-
-
- ); +export function PageTOCPopover(props: ComponentProps<"div">) { + const ref = useRef(null); + const [open, setOpen] = useState(false); + const { collapsed } = useSidebar(); + const { isTransparent } = useNav(); + const tocNavId = useId(); + + const onClick = useEffectEvent((e: Event) => { + if (!open) return; + + if (ref.current && !ref.current.contains(e.target as HTMLElement)) + setOpen(false); + }); + + useEffect(() => { + window.addEventListener("click", onClick); + + return () => { + window.removeEventListener("click", onClick); + }; + }, [onClick]); + + return ( + ({ + open, + setOpen, + }), + [open], + )} + > + +
+ {props.children} +
+
+
+ ); } export function PageLastUpdate({ - date: value, - ...props -}: Omit, 'children'> & { date: Date | string }) { - const { text } = useI18n(); - const [date, setDate] = useState(''); - - useEffect(() => { - // to the timezone of client - setDate(new Date(value).toLocaleDateString()); - }, [value]); - - return ( -

- {text.lastUpdate} {date} -

- ); + date: value, + ...props +}: Omit, "children"> & { date: Date | string }) { + const { text } = useI18n(); + const [date, setDate] = useState(""); + + useEffect(() => { + // to the timezone of client + setDate(new Date(value).toLocaleDateString()); + }, [value]); + + return ( +

+ {text.lastUpdate} {date} +

+ ); } -type Item = Pick; -export interface FooterProps extends ComponentProps<'div'> { - /** - * Items including information for the next and previous page - */ - items?: { - previous?: Item; - next?: Item; - }; +type Item = Pick; +export interface FooterProps extends ComponentProps<"div"> { + /** + * Items including information for the next and previous page + */ + items?: { + previous?: Item; + next?: Item; + }; } function scanNavigationList(tree: PageTree.Node[]) { - const list: PageTree.Item[] = []; + const list: PageTree.Item[] = []; - tree.forEach((node) => { - if (node.type === 'folder') { - if (node.index) { - list.push(node.index); - } + tree.forEach((node) => { + if (node.type === "folder") { + if (node.index) { + list.push(node.index); + } - list.push(...scanNavigationList(node.children)); - return; - } + list.push(...scanNavigationList(node.children)); + return; + } - if (node.type === 'page' && !node.external) { - list.push(node); - } - }); + if (node.type === "page" && !node.external) { + list.push(node); + } + }); - return list; + return list; } const listCache = new Map(); export function PageFooter({ items, ...props }: FooterProps) { - const { root } = useTreeContext(); - const pathname = usePathname(); - - const { previous, next } = useMemo(() => { - if (items) return items; - - const cached = listCache.get(root.$id); - const list = cached ?? scanNavigationList(root.children); - listCache.set(root.$id, list); - - const idx = list.findIndex((item) => isActive(item.url, pathname, false)); - - if (idx === -1) return {}; - return { - previous: list[idx - 1], - next: list[idx + 1], - }; - }, [items, pathname, root]); - - return ( -
- {previous ? : null} - {next ? : null} -
- ); + const { root } = useTreeContext(); + const pathname = usePathname(); + + const { previous, next } = useMemo(() => { + if (items) return items; + + const cached = listCache.get(root.$id); + const list = cached ?? scanNavigationList(root.children); + listCache.set(root.$id, list); + + const idx = list.findIndex((item) => isActive(item.url, pathname, false)); + + if (idx === -1) return {}; + return { + previous: list[idx - 1], + next: list[idx + 1], + }; + }, [items, pathname, root]); + + return ( +
+ {previous ? : null} + {next ? : null} +
+ ); } function FooterItem({ item, index }: { item: Item; index: 0 | 1 }) { - const { text } = useI18n(); - const Icon = index === 0 ? ChevronLeft : ChevronRight; - - return ( - -
- -

{item.name}

-
-

- {item.description ?? (index === 0 ? text.previousPage : text.nextPage)} -

- - ); + const { text } = useI18n(); + const Icon = index === 0 ? ChevronLeft : ChevronRight; + + return ( + +
+ +

{item.name}

+
+

+ {item.description ?? (index === 0 ? text.previousPage : text.nextPage)} +

+ + ); } -export type BreadcrumbProps = BreadcrumbOptions & ComponentProps<'div'>; +export type BreadcrumbProps = BreadcrumbOptions & ComponentProps<"div">; export function PageBreadcrumb({ - includeRoot = false, - includeSeparator, - includePage = false, - ...props + includeRoot = false, + includeSeparator, + includePage = false, + ...props }: BreadcrumbProps) { - const path = useTreePath(); - const { root } = useTreeContext(); - const items = useMemo(() => { - return getBreadcrumbItemsFromPath(root, path, { - includePage, - includeSeparator, - includeRoot, - }); - }, [includePage, includeRoot, includeSeparator, path, root]); - - if (items.length === 0) return null; - - return ( -
- {items.map((item, i) => { - const className = cn( - 'truncate', - i === items.length - 1 && 'text-fd-primary font-medium', - ); - - return ( - - {i !== 0 && } - {item.url ? ( - - {item.name} - - ) : ( - {item.name} - )} - - ); - })} -
- ); + const path = useTreePath(); + const { root } = useTreeContext(); + const items = useMemo(() => { + return getBreadcrumbItemsFromPath(root, path, { + includePage, + includeSeparator, + includeRoot, + }); + }, [includePage, includeRoot, includeSeparator, path, root]); + + if (items.length === 0) return null; + + return ( +
+ {items.map((item, i) => { + const className = cn( + "truncate", + i === items.length - 1 && "text-fd-primary font-medium", + ); + + return ( + + {i !== 0 && } + {item.url ? ( + + {item.name} + + ) : ( + {item.name} + )} + + ); + })} +
+ ); } -export function PageTOC(props: ComponentProps<'div'>) { - const { collapsed } = useSidebar(); - const offset = collapsed ? '0px' : 'var(--fd-layout-offset)'; - - return ( -
-
- {props.children} -
-
- ); +export function PageTOC(props: ComponentProps<"div">) { + const { collapsed } = useSidebar(); + const offset = collapsed ? "0px" : "var(--fd-layout-offset)"; + const tocId = useId(); + + return ( +
+
+ {props.children} +
+
+ ); } diff --git a/src/components/layout/docs/page.tsx b/src/components/layout/docs/page.tsx index 38b85f9c..b307c92f 100644 --- a/src/components/layout/docs/page.tsx +++ b/src/components/layout/docs/page.tsx @@ -1,103 +1,104 @@ -import { type ComponentProps } from 'react'; -import { cn } from '../../../lib/cn'; +import type { AnchorProviderProps } from "fumadocs-core/toc"; +import { I18nLabel } from "fumadocs-ui/contexts/i18n"; +import { Text } from "lucide-react"; +import { type ComponentProps, useId } from "react"; +import { cn } from "../../../lib/cn"; +import { TOCItems, TOCProvider, TOCScrollArea } from "../../ui/toc"; +import ClerkTOCItems from "../../ui/toc-clerk"; import { - type BreadcrumbProps, - type FooterProps, - PageBreadcrumb, - PageFooter, - PageLastUpdate, - PageTOC, - PageTOCPopover, - PageTOCPopoverContent, - PageTOCPopoverTrigger, -} from './page-client'; -import { TOCItems, TOCProvider, TOCScrollArea } from '../../ui/toc'; -import { Text } from 'lucide-react'; -import { I18nLabel } from 'fumadocs-ui/contexts/i18n'; -import ClerkTOCItems from '../../ui/toc-clerk'; -import type { AnchorProviderProps } from 'fumadocs-core/toc'; + type BreadcrumbProps, + type FooterProps, + PageBreadcrumb, + PageFooter, + PageLastUpdate, + PageTOC, + PageTOCPopover, + PageTOCPopoverContent, + PageTOCPopoverTrigger, +} from "./page-client"; -export function PageTOCTitle(props: ComponentProps<'h2'>) { - return ( -

- - -

- ); +export function PageTOCTitle(props: ComponentProps<"h2">) { + return ( +

+ + +

+ ); } export function PageTOCItems({ - variant = 'normal', - ...props -}: ComponentProps<'div'> & { variant?: 'clerk' | 'normal' }) { - return ( - - {variant === 'clerk' ? : } - - ); + variant = "normal", + ...props +}: ComponentProps<"div"> & { variant?: "clerk" | "normal" }) { + return ( + + {variant === "clerk" ? : } + + ); } export function PageTOCPopoverItems({ - variant = 'normal', - ...props -}: ComponentProps<'div'> & { variant?: 'clerk' | 'normal' }) { - return ( - - {variant === 'clerk' ? : } - - ); + variant = "normal", + ...props +}: ComponentProps<"div"> & { variant?: "clerk" | "normal" }) { + return ( + + {variant === "clerk" ? : } + + ); } -export function PageArticle(props: ComponentProps<'article'>) { - return ( -
- {props.children} -
- ); +export function PageArticle(props: ComponentProps<"article">) { + return ( +
+ {props.children} +
+ ); } -export interface RootProps extends ComponentProps<'div'> { - toc?: Omit | false; +export interface RootProps extends ComponentProps<"div"> { + toc?: Omit | false; } export function PageRoot({ toc = false, children, ...props }: RootProps) { - const content = ( -
- {children} -
- ); + const pageId = useId(); + const content = ( +
+ {children} +
+ ); - if (toc) return {content}; - return content; + if (toc) return {content}; + return content; } export { - PageBreadcrumb, - PageFooter, - PageLastUpdate, - PageTOC, - PageTOCPopover, - PageTOCPopoverTrigger, - PageTOCPopoverContent, - type FooterProps, - type BreadcrumbProps, + PageBreadcrumb, + PageFooter, + PageLastUpdate, + PageTOC, + PageTOCPopover, + PageTOCPopoverTrigger, + PageTOCPopoverContent, + type FooterProps, + type BreadcrumbProps, }; diff --git a/src/components/layout/page.tsx b/src/components/layout/page.tsx index 05bccbed..3a0e410a 100644 --- a/src/components/layout/page.tsx +++ b/src/components/layout/page.tsx @@ -1,288 +1,288 @@ -import { type ComponentProps, forwardRef, type ReactNode } from 'react'; -import { cn } from '../../lib/cn'; -import { buttonVariants } from '../ui/button'; -import { Edit } from 'lucide-react'; -import { I18nLabel } from 'fumadocs-ui/contexts/i18n'; +import type { TOCItemType } from "fumadocs-core/server"; +import type { AnchorProviderProps } from "fumadocs-core/toc"; +import { I18nLabel } from "fumadocs-ui/contexts/i18n"; +import { Edit } from "lucide-react"; +import { type ComponentProps, forwardRef, type ReactNode } from "react"; +import { cn } from "../../lib/cn"; +import { buttonVariants } from "../ui/button"; import { - type BreadcrumbProps, - type FooterProps, - PageArticle, - PageBreadcrumb, - PageFooter, - PageLastUpdate, - PageRoot, - PageTOC, - PageTOCItems, - PageTOCPopover, - PageTOCPopoverContent, - PageTOCPopoverItems, - PageTOCPopoverTrigger, - PageTOCTitle, -} from './docs/page'; -import type { AnchorProviderProps } from 'fumadocs-core/toc'; -import type { TOCItemType } from 'fumadocs-core/server'; + type BreadcrumbProps, + type FooterProps, + PageArticle, + PageBreadcrumb, + PageFooter, + PageLastUpdate, + PageRoot, + PageTOC, + PageTOCItems, + PageTOCPopover, + PageTOCPopoverContent, + PageTOCPopoverItems, + PageTOCPopoverTrigger, + PageTOCTitle, +} from "./docs/page"; interface EditOnGitHubOptions - extends Omit, 'href' | 'children'> { - owner: string; - repo: string; + extends Omit, "href" | "children"> { + owner: string; + repo: string; - /** - * SHA or ref (branch or tag) name. - * - * @defaultValue main - */ - sha?: string; + /** + * SHA or ref (branch or tag) name. + * + * @defaultValue main + */ + sha?: string; - /** - * File path in the repo - */ - path: string; + /** + * File path in the repo + */ + path: string; } interface BreadcrumbOptions extends BreadcrumbProps { - enabled: boolean; - component: ReactNode; + enabled: boolean; + component: ReactNode; - /** - * Show the full path to the current page - * - * @defaultValue false - * @deprecated use `includePage` instead - */ - full?: boolean; + /** + * Show the full path to the current page + * + * @defaultValue false + * @deprecated use `includePage` instead + */ + full?: boolean; } interface FooterOptions extends FooterProps { - enabled: boolean; - component: ReactNode; + enabled: boolean; + component: ReactNode; } export interface DocsPageProps { - toc?: TOCItemType[]; - tableOfContent?: Partial; - tableOfContentPopover?: Partial; + toc?: TOCItemType[]; + tableOfContent?: Partial; + tableOfContentPopover?: Partial; - /** - * Extend the page to fill all available space - * - * @defaultValue false - */ - full?: boolean; + /** + * Extend the page to fill all available space + * + * @defaultValue false + */ + full?: boolean; - /** - * Replace or disable breadcrumb - */ - breadcrumb?: Partial; + /** + * Replace or disable breadcrumb + */ + breadcrumb?: Partial; - /** - * Footer navigation, you can disable it by passing `false` - */ - footer?: Partial; + /** + * Footer navigation, you can disable it by passing `false` + */ + footer?: Partial; - editOnGithub?: EditOnGitHubOptions; - lastUpdate?: Date | string | number; + editOnGithub?: EditOnGitHubOptions; + lastUpdate?: Date | string | number; - container?: ComponentProps<'div'>; - article?: ComponentProps<'article'>; - children?: ReactNode; + container?: ComponentProps<"div">; + article?: ComponentProps<"article">; + children?: ReactNode; } -type TableOfContentOptions = Pick & { - /** - * Custom content in TOC container, before the main TOC - */ - header?: ReactNode; +type TableOfContentOptions = Pick & { + /** + * Custom content in TOC container, before the main TOC + */ + header?: ReactNode; - /** - * Custom content in TOC container, after the main TOC - */ - footer?: ReactNode; + /** + * Custom content in TOC container, after the main TOC + */ + footer?: ReactNode; - enabled: boolean; - component: ReactNode; + enabled: boolean; + component: ReactNode; - /** - * @defaultValue 'normal' - */ - style?: 'normal' | 'clerk'; + /** + * @defaultValue 'normal' + */ + style?: "normal" | "clerk"; }; -type TableOfContentPopoverOptions = Omit; +type TableOfContentPopoverOptions = Omit; export function DocsPage({ - editOnGithub, - breadcrumb: { - enabled: breadcrumbEnabled = true, - component: breadcrumb, - ...breadcrumbProps - } = {}, - footer = {}, - lastUpdate, - container, - full = false, - tableOfContentPopover: { - enabled: tocPopoverEnabled, - component: tocPopover, - ...tocPopoverOptions - } = {}, - tableOfContent: { - enabled: tocEnabled, - component: tocReplace, - ...tocOptions - } = {}, - toc = [], - article, - children, + editOnGithub, + breadcrumb: { + enabled: breadcrumbEnabled = true, + component: breadcrumb, + ...breadcrumbProps + } = {}, + footer = {}, + lastUpdate, + container, + full = false, + tableOfContentPopover: { + enabled: tocPopoverEnabled, + component: tocPopover, + ...tocPopoverOptions + } = {}, + tableOfContent: { + enabled: tocEnabled, + component: tocReplace, + ...tocOptions + } = {}, + toc = [], + article, + children, }: DocsPageProps) { - // disable TOC on full mode, you can still enable it with `enabled` option. - tocEnabled ??= - !full && - (toc.length > 0 || - tocOptions.footer !== undefined || - tocOptions.header !== undefined); + // disable TOC on full mode, you can still enable it with `enabled` option. + tocEnabled ??= + !full && + (toc.length > 0 || + tocOptions.footer !== undefined || + tocOptions.header !== undefined); - tocPopoverEnabled ??= - toc.length > 0 || - tocPopoverOptions.header !== undefined || - tocPopoverOptions.footer !== undefined; + tocPopoverEnabled ??= + toc.length > 0 || + tocPopoverOptions.header !== undefined || + tocPopoverOptions.footer !== undefined; - return ( - - {tocPopoverEnabled && - (tocPopover ?? ( - - - - {tocPopoverOptions.header} - - {tocPopoverOptions.footer} - - - ))} - - {breadcrumbEnabled && - (breadcrumb ?? )} - {children} -
- {editOnGithub && ( - - )} - {lastUpdate && } -
- {footer.enabled !== false && - (footer.component ?? )} -
- {tocEnabled && - (tocReplace ?? ( - - {tocOptions.header} - - - {tocOptions.footer} - - ))} -
- ); + return ( + + {tocPopoverEnabled && + (tocPopover ?? ( + + + + {tocPopoverOptions.header} + + {tocPopoverOptions.footer} + + + ))} + + {breadcrumbEnabled && + (breadcrumb ?? )} + {children} +
+ {editOnGithub && ( + + )} + {lastUpdate && } +
+ {footer.enabled !== false && + (footer.component ?? )} +
+ {tocEnabled && + (tocReplace ?? ( + + {tocOptions.header} + + + {tocOptions.footer} + + ))} +
+ ); } -export function EditOnGitHub(props: ComponentProps<'a'>) { - return ( - - {props.children ?? ( - <> - - - - )} - - ); +export function EditOnGitHub(props: ComponentProps<"a">) { + return ( + + {props.children ?? ( + <> + + + + )} + + ); } /** * Add typography styles */ -export const DocsBody = forwardRef>( - (props, ref) => ( -
- {props.children} -
- ), +export const DocsBody = forwardRef>( + (props, ref) => ( +
+ {props.children} +
+ ), ); -DocsBody.displayName = 'DocsBody'; +DocsBody.displayName = "DocsBody"; export const DocsDescription = forwardRef< - HTMLParagraphElement, - ComponentProps<'p'> + HTMLParagraphElement, + ComponentProps<"p"> >((props, ref) => { - // don't render if no description provided - if (props.children === undefined) return null; + // don't render if no description provided + if (props.children === undefined) return null; - return ( -

- {props.children} -

- ); + return ( +

+ {props.children} +

+ ); }); -DocsDescription.displayName = 'DocsDescription'; +DocsDescription.displayName = "DocsDescription"; -export const DocsTitle = forwardRef>( - (props, ref) => { - return ( -

- {props.children} -

- ); - }, +export const DocsTitle = forwardRef>( + (props, ref) => { + return ( +

+ {props.children} +

+ ); + }, ); -DocsTitle.displayName = 'DocsTitle'; +DocsTitle.displayName = "DocsTitle"; /** * For separate MDX page */ -export function withArticle(props: ComponentProps<'main'>): ReactNode { - return ( -
-
{props.children}
-
- ); +export function withArticle(props: ComponentProps<"main">): ReactNode { + return ( +
+
{props.children}
+
+ ); } diff --git a/src/components/layout/shared/client.tsx b/src/components/layout/shared/client.tsx index d685015e..bd332c2e 100644 --- a/src/components/layout/shared/client.tsx +++ b/src/components/layout/shared/client.tsx @@ -1,30 +1,30 @@ -'use client'; -import type { ComponentProps } from 'react'; -import { usePathname } from 'fumadocs-core/framework'; -import { isActive } from '../../../lib/is-active'; -import Link from 'fumadocs-core/link'; -import type { BaseLinkType } from './index'; +"use client"; +import { usePathname } from "fumadocs-core/framework"; +import Link from "fumadocs-core/link"; +import type { ComponentProps } from "react"; +import { isActive } from "../../../lib/is-active"; +import type { BaseLinkType } from "./index"; export function BaseLinkItem({ - ref, - item, - ...props -}: Omit, 'href'> & { item: BaseLinkType }) { - const pathname = usePathname(); - const activeType = item.active ?? 'url'; - const active = - activeType !== 'none' && - isActive(item.url, pathname, activeType === 'nested-url'); + ref, + item, + ...props +}: Omit, "href"> & { item: BaseLinkType }) { + const pathname = usePathname(); + const activeType = item.active ?? "url"; + const active = + activeType !== "none" && + isActive(item.url, pathname, activeType === "nested-url"); - return ( - - {props.children} - - ); + return ( + + {props.children} + + ); } diff --git a/src/components/layout/shared/index.tsx b/src/components/layout/shared/index.tsx index c73090b6..33323066 100644 --- a/src/components/layout/shared/index.tsx +++ b/src/components/layout/shared/index.tsx @@ -1,182 +1,187 @@ -import type { HTMLAttributes, ReactNode } from 'react'; -import type { NavProviderProps } from 'fumadocs-ui/contexts/layout'; -import type { I18nConfig } from 'fumadocs-core/i18n'; +import type { I18nConfig } from "fumadocs-core/i18n"; +import type { NavProviderProps } from "fumadocs-ui/contexts/layout"; +import type { HTMLAttributes, ReactNode } from "react"; export interface NavOptions extends NavProviderProps { - enabled: boolean; - component: ReactNode; + enabled: boolean; + component: ReactNode; - title?: ReactNode; + title?: ReactNode; - /** - * Redirect url of title - * @defaultValue '/' - */ - url?: string; + /** + * Redirect url of title + * @defaultValue '/' + */ + url?: string; - children?: ReactNode; + children?: ReactNode; } export interface BaseLayoutProps { - themeSwitch?: { - enabled?: boolean; - component?: ReactNode; - mode?: 'light-dark' | 'light-dark-system'; - }; - - searchToggle?: Partial<{ - enabled: boolean; - components: Partial<{ - sm: ReactNode; - lg: ReactNode; - }>; - }>; - - /** - * Remove theme switcher component - * - * @deprecated Use `themeSwitch.enabled` instead. - */ - disableThemeSwitch?: boolean; - - /** - * I18n options - * - * @defaultValue false - */ - i18n?: boolean | I18nConfig; - - /** - * GitHub url - */ - githubUrl?: string; - - links?: LinkItemType[]; - /** - * Replace or disable navbar - */ - nav?: Partial; - - children?: ReactNode; + themeSwitch?: { + enabled?: boolean; + component?: ReactNode; + mode?: "light-dark" | "light-dark-system"; + }; + + searchToggle?: Partial<{ + enabled: boolean; + components: Partial<{ + sm: ReactNode; + lg: ReactNode; + }>; + }>; + + /** + * Remove theme switcher component + * + * @deprecated Use `themeSwitch.enabled` instead. + */ + disableThemeSwitch?: boolean; + + /** + * I18n options + * + * @defaultValue false + */ + i18n?: boolean | I18nConfig; + + /** + * GitHub url + */ + githubUrl?: string; + + links?: LinkItemType[]; + /** + * Replace or disable navbar + */ + nav?: Partial; + + children?: ReactNode; } interface BaseItem { - /** - * Restrict where the item is displayed - * - * @defaultValue 'all' - */ - on?: 'menu' | 'nav' | 'all'; + /** + * Restrict where the item is displayed + * + * @defaultValue 'all' + */ + on?: "menu" | "nav" | "all"; } export interface BaseLinkType extends BaseItem { - url: string; - /** - * When the item is marked as active - * - * @defaultValue 'url' - */ - active?: 'url' | 'nested-url' | 'none'; - external?: boolean; + url: string; + /** + * When the item is marked as active + * + * @defaultValue 'url' + */ + active?: "url" | "nested-url" | "none"; + external?: boolean; } export interface MainItemType extends BaseLinkType { - type?: 'main'; - icon?: ReactNode; - text: ReactNode; - description?: ReactNode; + type?: "main"; + icon?: ReactNode; + text: ReactNode; + description?: ReactNode; } export interface IconItemType extends BaseLinkType { - type: 'icon'; - /** - * `aria-label` of icon button - */ - label?: string; - icon: ReactNode; - text: ReactNode; - /** - * @defaultValue true - */ - secondary?: boolean; + type: "icon"; + /** + * `aria-label` of icon button + */ + label?: string; + icon: ReactNode; + text: ReactNode; + /** + * @defaultValue true + */ + secondary?: boolean; } export interface ButtonItemType extends BaseLinkType { - type: 'button'; - icon?: ReactNode; - text: ReactNode; - /** - * @defaultValue false - */ - secondary?: boolean; + type: "button"; + icon?: ReactNode; + text: ReactNode; + /** + * @defaultValue false + */ + secondary?: boolean; } export interface MenuItemType extends Partial { - type: 'menu'; - icon?: ReactNode; - text: ReactNode; - - items: ( - | (MainItemType & { - /** - * Options when displayed on navigation menu - */ - menu?: HTMLAttributes & { - banner?: ReactNode; - }; - }) - | CustomItemType - )[]; - - /** - * @defaultValue false - */ - secondary?: boolean; + type: "menu"; + icon?: ReactNode; + text: ReactNode; + + items: ( + | (MainItemType & { + /** + * Options when displayed on navigation menu + */ + menu?: HTMLAttributes & { + banner?: ReactNode; + }; + }) + | CustomItemType + )[]; + + /** + * @defaultValue false + */ + secondary?: boolean; } export interface CustomItemType extends BaseItem { - type: 'custom'; - /** - * @defaultValue false - */ - secondary?: boolean; - children: ReactNode; + type: "custom"; + /** + * @defaultValue false + */ + secondary?: boolean; + children: ReactNode; } export type LinkItemType = - | MainItemType - | IconItemType - | ButtonItemType - | MenuItemType - | CustomItemType; + | MainItemType + | IconItemType + | ButtonItemType + | MenuItemType + | CustomItemType; /** * Get Links Items with shortcuts */ export function getLinks( - links: LinkItemType[] = [], - githubUrl?: string, + links: LinkItemType[] = [], + githubUrl?: string, ): LinkItemType[] { - let result = links ?? []; - - if (githubUrl) - result = [ - ...result, - { - type: 'icon', - url: githubUrl, - text: 'Github', - label: 'GitHub', - icon: ( - - - - ), - external: true, - }, - ]; - - return result; + let result = links ?? []; + + if (githubUrl) + result = [ + ...result, + { + type: "icon", + url: githubUrl, + text: "Github", + label: "GitHub", + icon: ( + + + + ), + external: true, + }, + ]; + + return result; } -export { BaseLinkItem } from './client'; +export { BaseLinkItem } from "./client"; diff --git a/src/components/root-toggle.tsx b/src/components/root-toggle.tsx index 02fed5df..756a7ec2 100644 --- a/src/components/root-toggle.tsx +++ b/src/components/root-toggle.tsx @@ -1,103 +1,103 @@ -'use client'; -import { Check, ChevronsUpDown } from 'lucide-react'; -import { type ComponentProps, type ReactNode, useMemo, useState } from 'react'; -import Link from 'fumadocs-core/link'; -import { usePathname } from 'fumadocs-core/framework'; -import { cn } from '../lib/cn'; -import { isTabActive } from '../lib/is-active'; -import { useSidebar } from 'fumadocs-ui/contexts/sidebar'; -import { Popover, PopoverContent, PopoverTrigger } from './ui/popover'; -import type { SidebarTab } from 'fumadocs-ui/utils/get-sidebar-tabs'; +"use client"; +import { usePathname } from "fumadocs-core/framework"; +import Link from "fumadocs-core/link"; +import { useSidebar } from "fumadocs-ui/contexts/sidebar"; +import type { SidebarTab } from "fumadocs-ui/utils/get-sidebar-tabs"; +import { Check, ChevronsUpDown } from "lucide-react"; +import { type ComponentProps, type ReactNode, useMemo, useState } from "react"; +import { cn } from "../lib/cn"; +import { isTabActive } from "../lib/is-active"; +import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover"; export interface Option extends SidebarTab { - props?: ComponentProps<'a'>; + props?: ComponentProps<"a">; } export function RootToggle({ - options, - placeholder, - ...props + options, + placeholder, + ...props }: { - placeholder?: ReactNode; - options: Option[]; -} & ComponentProps<'button'>) { - const [open, setOpen] = useState(false); - const { closeOnRedirect } = useSidebar(); - const pathname = usePathname(); + placeholder?: ReactNode; + options: Option[]; +} & ComponentProps<"button">) { + const [open, setOpen] = useState(false); + const { closeOnRedirect } = useSidebar(); + const pathname = usePathname(); - const selected = useMemo(() => { - return options.findLast((item) => isTabActive(item, pathname)); - }, [options, pathname]); + const selected = useMemo(() => { + return options.findLast((item) => isTabActive(item, pathname)); + }, [options, pathname]); - const onClick = () => { - closeOnRedirect.current = false; - setOpen(false); - }; + const onClick = () => { + closeOnRedirect.current = false; + setOpen(false); + }; - const item = selected ? ( - <> -
{selected.icon}
-
-

{selected.title}

-

- {selected.description} -

-
- - ) : ( - placeholder - ); + const item = selected ? ( + <> +
{selected.icon}
+
+

{selected.title}

+

+ {selected.description} +

+
+ + ) : ( + placeholder + ); - return ( - - {item && ( - - {item} - - - )} - - {options.map((item) => { - const isActive = selected && item.url === selected.url; - if (!isActive && item.unlisted) return; + return ( + + {item && ( + + {item} + + + )} + + {options.map((item) => { + const isActive = selected && item.url === selected.url; + if (!isActive && item.unlisted) return null; - return ( - -
- {item.icon} -
-
-

{item.title}

-

- {item.description} -

-
+ return ( + +
+ {item.icon} +
+
+

{item.title}

+

+ {item.description} +

+
- - - ); - })} -
-
- ); + + + ); + })} +
+
+ ); } diff --git a/src/components/search-toggle.tsx b/src/components/search-toggle.tsx index 09529401..61ef4248 100644 --- a/src/components/search-toggle.tsx +++ b/src/components/search-toggle.tsx @@ -1,79 +1,82 @@ -'use client'; -import type { ComponentProps } from 'react'; -import { Search } from 'lucide-react'; -import { useSearchContext } from 'fumadocs-ui/contexts/search'; -import { useI18n } from 'fumadocs-ui/contexts/i18n'; -import { cn } from '../lib/cn'; -import { type ButtonProps, buttonVariants } from './ui/button'; +"use client"; +import { useI18n } from "fumadocs-ui/contexts/i18n"; +import { useSearchContext } from "fumadocs-ui/contexts/search"; +import { Search } from "lucide-react"; +import type { ComponentProps } from "react"; +import { cn } from "../lib/cn"; +import { type ButtonProps, buttonVariants } from "./ui/button"; interface SearchToggleProps - extends Omit, 'color'>, - ButtonProps { - hideIfDisabled?: boolean; + extends Omit, "color">, + ButtonProps { + hideIfDisabled?: boolean; } export function SearchToggle({ - hideIfDisabled, - size = 'icon-sm', - color = 'ghost', - ...props + hideIfDisabled, + size = "icon-sm", + color = "ghost", + ...props }: SearchToggleProps) { - const { setOpenSearch, enabled } = useSearchContext(); - if (hideIfDisabled && !enabled) return null; + const { setOpenSearch, enabled } = useSearchContext(); + if (hideIfDisabled && !enabled) return null; - return ( - - ); + return ( + + ); } export function LargeSearchToggle({ - hideIfDisabled, - ...props -}: ComponentProps<'button'> & { - hideIfDisabled?: boolean; + hideIfDisabled, + ...props +}: ComponentProps<"button"> & { + hideIfDisabled?: boolean; }) { - const { enabled, hotKey, setOpenSearch } = useSearchContext(); - const { text } = useI18n(); - if (hideIfDisabled && !enabled) return null; + const { enabled, hotKey, setOpenSearch } = useSearchContext(); + const { text } = useI18n(); + if (hideIfDisabled && !enabled) return null; - return ( - - ); + return ( + + ); } diff --git a/src/components/sidebar.tsx b/src/components/sidebar.tsx index 995debfb..7848d772 100644 --- a/src/components/sidebar.tsx +++ b/src/components/sidebar.tsx @@ -1,559 +1,573 @@ -'use client'; -import { ChevronDown, ExternalLink } from 'lucide-react'; -import { usePathname } from 'fumadocs-core/framework'; +"use client"; +import type { + CollapsibleContentProps, + CollapsibleTriggerProps, +} from "@radix-ui/react-collapsible"; +import { Presence } from "@radix-ui/react-presence"; +import type { ScrollAreaProps } from "@radix-ui/react-scroll-area"; +import { cva } from "class-variance-authority"; +import { usePathname } from "fumadocs-core/framework"; +import Link, { type LinkProps } from "fumadocs-core/link"; +import type { PageTree } from "fumadocs-core/server"; +import { useMediaQuery } from "fumadocs-core/utils/use-media-query"; +import { useOnChange } from "fumadocs-core/utils/use-on-change"; +import { useSidebar } from "fumadocs-ui/contexts/sidebar"; +import { useTreeContext, useTreePath } from "fumadocs-ui/contexts/tree"; +import { ChevronDown, ExternalLink } from "lucide-react"; import { - type ComponentProps, - createContext, - type FC, - Fragment, - type ReactNode, - useContext, - useMemo, - useRef, - useState, -} from 'react'; -import Link, { type LinkProps } from 'fumadocs-core/link'; -import { useOnChange } from 'fumadocs-core/utils/use-on-change'; -import { cn } from '../lib/cn'; -import { ScrollArea, ScrollViewport } from './ui/scroll-area'; -import { isActive } from '../lib/is-active'; + type ComponentProps, + createContext, + type FC, + Fragment, + type ReactNode, + useContext, + useId, + useMemo, + useRef, + useState, +} from "react"; +import { cn } from "../lib/cn"; +import { isActive } from "../lib/is-active"; import { - Collapsible, - CollapsibleContent, - CollapsibleTrigger, -} from './ui/collapsible'; -import { type ScrollAreaProps } from '@radix-ui/react-scroll-area'; -import { useSidebar } from 'fumadocs-ui/contexts/sidebar'; -import { cva } from 'class-variance-authority'; -import type { - CollapsibleContentProps, - CollapsibleTriggerProps, -} from '@radix-ui/react-collapsible'; -import type { PageTree } from 'fumadocs-core/server'; -import { useTreeContext, useTreePath } from 'fumadocs-ui/contexts/tree'; -import { useMediaQuery } from 'fumadocs-core/utils/use-media-query'; -import { Presence } from '@radix-ui/react-presence'; + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from "./ui/collapsible"; +import { ScrollArea, ScrollViewport } from "./ui/scroll-area"; export interface SidebarProps { - /** - * Open folders by default if their level is lower or equal to a specific level - * (Starting from 1) - * - * @defaultValue 0 - */ - defaultOpenLevel?: number; - - /** - * Prefetch links - * - * @defaultValue true - */ - prefetch?: boolean; - - /** - * Children to render - */ - Content: ReactNode; - - /** - * Alternative children for mobile - */ - Mobile?: ReactNode; + /** + * Open folders by default if their level is lower or equal to a specific level + * (Starting from 1) + * + * @defaultValue 0 + */ + defaultOpenLevel?: number; + + /** + * Prefetch links + * + * @defaultValue true + */ + prefetch?: boolean; + + /** + * Children to render + */ + Content: ReactNode; + + /** + * Alternative children for mobile + */ + Mobile?: ReactNode; } interface InternalContext { - defaultOpenLevel: number; - prefetch: boolean; - level: number; + defaultOpenLevel: number; + prefetch: boolean; + level: number; } const itemVariants = cva( - 'relative flex flex-row items-center gap-2 rounded-lg p-2 ps-(--sidebar-item-offset) text-start text-fd-muted-foreground [overflow-wrap:anywhere] [&_svg]:size-4 [&_svg]:shrink-0', - { - variants: { - active: { - true: 'bg-fd-primary/10 text-fd-primary', - false: - 'transition-colors hover:bg-fd-accent/50 hover:text-fd-accent-foreground/80 hover:transition-none', - }, - }, - }, + "relative flex flex-row items-center gap-2 rounded-lg p-2 ps-(--sidebar-item-offset) text-start text-fd-muted-foreground [overflow-wrap:anywhere] [&_svg]:size-4 [&_svg]:shrink-0", + { + variants: { + active: { + true: "bg-fd-primary/10 text-fd-primary", + false: + "transition-colors hover:bg-fd-accent/50 hover:text-fd-accent-foreground/80 hover:transition-none", + }, + }, + }, ); const Context = createContext(null); const FolderContext = createContext<{ - open: boolean; - setOpen: React.Dispatch>; + open: boolean; + setOpen: React.Dispatch>; } | null>(null); export function Sidebar({ - defaultOpenLevel = 0, - prefetch = true, - Mobile, - Content, + defaultOpenLevel = 0, + prefetch = true, + Mobile, + Content, }: SidebarProps) { - const isMobile = useMediaQuery('(width < 768px)') ?? false; - const context = useMemo(() => { - return { - defaultOpenLevel, - prefetch, - level: 1, - }; - }, [defaultOpenLevel, prefetch]); - - return ( - - {isMobile && Mobile != null ? Mobile : Content} - - ); + const isMobile = useMediaQuery("(width < 768px)") ?? false; + const context = useMemo(() => { + return { + defaultOpenLevel, + prefetch, + level: 1, + }; + }, [defaultOpenLevel, prefetch]); + + return ( + + {isMobile && Mobile != null ? Mobile : Content} + + ); } -export function SidebarContent(props: ComponentProps<'aside'>) { - const { collapsed } = useSidebar(); - const [hover, setHover] = useState(false); - const timerRef = useRef(0); - const closeTimeRef = useRef(0); - - useOnChange(collapsed, () => { - setHover(false); - closeTimeRef.current = Date.now() + 150; - }); - - return ( - - ); +export function SidebarContent(props: ComponentProps<"aside">) { + const { collapsed } = useSidebar(); + const [hover, setHover] = useState(false); + const timerRef = useRef(0); + const closeTimeRef = useRef(0); + const sidebarId = useId(); + + useOnChange(collapsed, () => { + setHover(false); + closeTimeRef.current = Date.now() + 150; + }); + + return ( + + ); } export function SidebarContentMobile({ - className, - children, - ...props -}: ComponentProps<'aside'>) { - const { open, setOpen } = useSidebar(); - const state = open ? 'open' : 'closed'; - - return ( - <> - -
setOpen(false)} - /> - - - {({ present }) => ( - - )} - - - ); + className, + children, + ...props +}: ComponentProps<"aside">) { + const { open, setOpen } = useSidebar(); + const state = open ? "open" : "closed"; + const mobileSidebarId = useId(); + + return ( + <> + + - ); + children, + ...props +}: ComponentProps<"button">) { + const { setOpen } = useSidebar(); + + return ( + + ); } -export function SidebarCollapseTrigger(props: ComponentProps<'button'>) { - const { collapsed, setCollapsed } = useSidebar(); - - return ( - - ); +export function SidebarCollapseTrigger(props: ComponentProps<"button">) { + const { collapsed, setCollapsed } = useSidebar(); + + return ( + + ); } function useFolderContext() { - const ctx = useContext(FolderContext); - if (!ctx) throw new Error('Missing sidebar folder'); + const ctx = useContext(FolderContext); + if (!ctx) throw new Error("Missing sidebar folder"); - return ctx; + return ctx; } function useInternalContext() { - const ctx = useContext(Context); - if (!ctx) throw new Error(' component required.'); + const ctx = useContext(Context); + if (!ctx) throw new Error(" component required."); - return ctx; + return ctx; } export interface SidebarComponents { - Item: FC<{ item: PageTree.Item }>; - Folder: FC<{ item: PageTree.Folder; level: number; children: ReactNode }>; - Separator: FC<{ item: PageTree.Separator }>; + Item: FC<{ item: PageTree.Item }>; + Folder: FC<{ item: PageTree.Folder; level: number; children: ReactNode }>; + Separator: FC<{ item: PageTree.Separator }>; } /** * Render sidebar items from page tree */ export function SidebarPageTree(props: { - components?: Partial; + components?: Partial; }) { - const { root } = useTreeContext(); - - return useMemo(() => { - const { Separator, Item, Folder } = props.components ?? {}; - - function renderSidebarList( - items: PageTree.Node[], - level: number, - ): ReactNode[] { - return items.map((item, i) => { - if (item.type === 'separator') { - if (Separator) return ; - return ( - - {item.icon} - {item.name} - - ); - } - - if (item.type === 'folder') { - const children = renderSidebarList(item.children, level + 1); - - if (Folder) - return ( - - {children} - - ); - return ( - - {children} - - ); - } - - if (Item) return ; - return ( - - {item.name} - - ); - }); - } - - return ( - {renderSidebarList(root.children, 1)} - ); - }, [props.components, root]); + const { root } = useTreeContext(); + + return useMemo(() => { + const { Separator, Item, Folder } = props.components ?? {}; + + function renderSidebarList( + items: PageTree.Node[], + level: number, + ): ReactNode[] { + return items.map((item, i) => { + if (item.type === "separator") { + const key = `separator-${i}`; + if (Separator) return ; + return ( + + {item.icon} + {item.name} + + ); + } + + if (item.type === "folder") { + const children = renderSidebarList( + (item as PageTree.Folder).children || [], + level + 1, + ); + const key = `folder-${item.name || item.$id || i}`; + + if (Folder) + return ( + + {children} + + ); + return ( + + {children} + + ); + } + + if (Item) return ; + return ( + + {item.name} + + ); + }); + } + + return ( + {renderSidebarList(root.children, 1)} + ); + }, [props.components, root]); } function PageTreeFolder({ - item, - ...props + item, + ...props }: { - item: PageTree.Folder; - children: ReactNode; + item: PageTree.Folder; + children: ReactNode; }) { - const { defaultOpenLevel, level } = useInternalContext(); - const path = useTreePath(); - - return ( - = level) || path.includes(item) - } - > - {item.index ? ( - - {item.icon} - {item.name} - - ) : ( - - {item.icon} - {item.name} - - )} - {props.children} - - ); + const { defaultOpenLevel, level } = useInternalContext(); + const path = useTreePath(); + + return ( + = level) || path.includes(item) + } + > + {item.index ? ( + + {item.icon} + {item.name} + + ) : ( + + {item.icon} + {item.name} + + )} + {props.children} + + ); } diff --git a/src/components/theme-toggle.tsx b/src/components/theme-toggle.tsx index 4e6656cb..4be80c32 100644 --- a/src/components/theme-toggle.tsx +++ b/src/components/theme-toggle.tsx @@ -1,87 +1,88 @@ -'use client'; -import { cva } from 'class-variance-authority'; -import { Moon, Sun, Airplay } from 'lucide-react'; -import { useTheme } from 'next-themes'; -import { type HTMLAttributes, useLayoutEffect, useState } from 'react'; -import { cn } from '../lib/cn'; +"use client"; +import { cva } from "class-variance-authority"; +import { Airplay, Moon, Sun } from "lucide-react"; +import { useTheme } from "next-themes"; +import { type HTMLAttributes, useLayoutEffect, useState } from "react"; +import { cn } from "../lib/cn"; const itemVariants = cva( - 'size-6.5 rounded-full p-1.5 text-fd-muted-foreground', - { - variants: { - active: { - true: 'bg-fd-accent text-fd-accent-foreground', - false: 'text-fd-muted-foreground', - }, - }, - }, + "size-6.5 rounded-full p-1.5 text-fd-muted-foreground", + { + variants: { + active: { + true: "bg-fd-accent text-fd-accent-foreground", + false: "text-fd-muted-foreground", + }, + }, + }, ); const full = [ - ['light', Sun] as const, - ['dark', Moon] as const, - ['system', Airplay] as const, + ["light", Sun] as const, + ["dark", Moon] as const, + ["system", Airplay] as const, ]; export function ThemeToggle({ - className, - mode = 'light-dark', - ...props + className, + mode = "light-dark", + ...props }: HTMLAttributes & { - mode?: 'light-dark' | 'light-dark-system'; + mode?: "light-dark" | "light-dark-system"; }) { - const { setTheme, theme, resolvedTheme } = useTheme(); - const [mounted, setMounted] = useState(false); + const { setTheme, theme, resolvedTheme } = useTheme(); + const [mounted, setMounted] = useState(false); - useLayoutEffect(() => { - setMounted(true); - }, []); + useLayoutEffect(() => { + setMounted(true); + }, []); - const container = cn( - 'inline-flex items-center rounded-full border p-1', - className, - ); + const container = cn( + "inline-flex items-center rounded-full border p-1", + className, + ); - if (mode === 'light-dark') { - const value = mounted ? resolvedTheme : null; + if (mode === "light-dark") { + const value = mounted ? resolvedTheme : null; - return ( - - ); - } + return ( + + ); + })} + + ); + } - const value = mounted ? theme : null; + const value = mounted ? theme : null; - return ( -
- {full.map(([key, Icon]) => ( - - ))} -
- ); + return ( +
+ {full.map(([key, Icon]) => ( + + ))} +
+ ); } diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx index 5fca2adb..7df62b94 100644 --- a/src/components/ui/button.tsx +++ b/src/components/ui/button.tsx @@ -1,28 +1,28 @@ -import { cva, type VariantProps } from 'class-variance-authority'; +import { cva, type VariantProps } from "class-variance-authority"; const variants = { - primary: 'bg-fd-primary text-fd-primary-foreground hover:bg-fd-primary/80', - outline: 'border hover:bg-fd-accent hover:text-fd-accent-foreground', - ghost: 'hover:bg-fd-accent hover:text-fd-accent-foreground', - secondary: - 'border bg-fd-secondary text-fd-secondary-foreground hover:bg-fd-accent hover:text-fd-accent-foreground', + primary: "bg-fd-primary text-fd-primary-foreground hover:bg-fd-primary/80", + outline: "border hover:bg-fd-accent hover:text-fd-accent-foreground", + ghost: "hover:bg-fd-accent hover:text-fd-accent-foreground", + secondary: + "border bg-fd-secondary text-fd-secondary-foreground hover:bg-fd-accent hover:text-fd-accent-foreground", } as const; export const buttonVariants = cva( - 'inline-flex items-center justify-center rounded-md p-2 text-sm font-medium transition-colors duration-100 disabled:pointer-events-none disabled:opacity-50 focus-visible:outline-none', - { - variants: { - variant: variants, - // fumadocs use `color` instead of `variant` - color: variants, - size: { - sm: 'gap-1 px-2 py-1.5 text-xs', - icon: 'p-1.5 [&_svg]:size-5', - 'icon-sm': 'p-1.5 [&_svg]:size-4.5', - 'icon-xs': 'p-1 [&_svg]:size-4', - }, - }, - }, + "inline-flex items-center justify-center rounded-md p-2 text-sm font-medium transition-colors duration-100 disabled:pointer-events-none disabled:opacity-50 focus-visible:outline-none", + { + variants: { + variant: variants, + // fumadocs use `color` instead of `variant` + color: variants, + size: { + sm: "gap-1 px-2 py-1.5 text-xs", + icon: "p-1.5 [&_svg]:size-5", + "icon-sm": "p-1.5 [&_svg]:size-4.5", + "icon-xs": "p-1 [&_svg]:size-4", + }, + }, + }, ); export type ButtonProps = VariantProps; diff --git a/src/components/ui/collapsible.tsx b/src/components/ui/collapsible.tsx index dbcf3f00..10a35ddc 100644 --- a/src/components/ui/collapsible.tsx +++ b/src/components/ui/collapsible.tsx @@ -1,39 +1,39 @@ -'use client'; -import * as CollapsiblePrimitive from '@radix-ui/react-collapsible'; -import { forwardRef, useEffect, useState } from 'react'; -import { cn } from '../../lib/cn'; +"use client"; +import * as CollapsiblePrimitive from "@radix-ui/react-collapsible"; +import { forwardRef, useEffect, useState } from "react"; +import { cn } from "../../lib/cn"; const Collapsible = CollapsiblePrimitive.Root; const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger; const CollapsibleContent = forwardRef< - HTMLDivElement, - React.ComponentPropsWithoutRef + HTMLDivElement, + React.ComponentPropsWithoutRef >(({ children, ...props }, ref) => { - const [mounted, setMounted] = useState(false); + const [mounted, setMounted] = useState(false); - useEffect(() => { - setMounted(true); - }, []); + useEffect(() => { + setMounted(true); + }, []); - return ( - - {children} - - ); + return ( + + {children} + + ); }); CollapsibleContent.displayName = - CollapsiblePrimitive.CollapsibleContent.displayName; + CollapsiblePrimitive.CollapsibleContent.displayName; export { Collapsible, CollapsibleTrigger, CollapsibleContent }; diff --git a/src/components/ui/popover.tsx b/src/components/ui/popover.tsx index e033f5d7..9722e042 100644 --- a/src/components/ui/popover.tsx +++ b/src/components/ui/popover.tsx @@ -1,29 +1,29 @@ -'use client'; -import * as PopoverPrimitive from '@radix-ui/react-popover'; -import * as React from 'react'; -import { cn } from '../../lib/cn'; +"use client"; +import * as PopoverPrimitive from "@radix-ui/react-popover"; +import * as React from "react"; +import { cn } from "../../lib/cn"; const Popover = PopoverPrimitive.Root; const PopoverTrigger = PopoverPrimitive.Trigger; const PopoverContent = React.forwardRef< - React.ComponentRef, - React.ComponentPropsWithoutRef ->(({ className, align = 'center', sideOffset = 4, ...props }, ref) => ( - - - + React.ComponentRef, + React.ComponentPropsWithoutRef +>(({ className, align = "center", sideOffset = 4, ...props }, ref) => ( + + + )); PopoverContent.displayName = PopoverPrimitive.Content.displayName; diff --git a/src/components/ui/scroll-area.tsx b/src/components/ui/scroll-area.tsx index 86cb150e..6684a872 100644 --- a/src/components/ui/scroll-area.tsx +++ b/src/components/ui/scroll-area.tsx @@ -1,57 +1,57 @@ -import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area'; -import * as React from 'react'; -import { cn } from '../../lib/cn'; +import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"; +import * as React from "react"; +import { cn } from "../../lib/cn"; const ScrollArea = React.forwardRef< - React.ComponentRef, - React.ComponentPropsWithoutRef + React.ComponentRef, + React.ComponentPropsWithoutRef >(({ className, children, ...props }, ref) => ( - - {children} - - - + + {children} + + + )); ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName; const ScrollViewport = React.forwardRef< - React.ComponentRef, - React.ComponentPropsWithoutRef + React.ComponentRef, + React.ComponentPropsWithoutRef >(({ className, children, ...props }, ref) => ( - - {children} - + + {children} + )); ScrollViewport.displayName = ScrollAreaPrimitive.Viewport.displayName; const ScrollBar = React.forwardRef< - React.ComponentRef, - React.ComponentPropsWithoutRef ->(({ className, orientation = 'vertical', ...props }, ref) => ( - - - + React.ComponentRef, + React.ComponentPropsWithoutRef +>(({ className, orientation = "vertical", ...props }, ref) => ( + + + )); ScrollBar.displayName = ScrollAreaPrimitive.Scrollbar.displayName; diff --git a/src/components/ui/toc-clerk.tsx b/src/components/ui/toc-clerk.tsx index f8466ab4..68e20877 100644 --- a/src/components/ui/toc-clerk.tsx +++ b/src/components/ui/toc-clerk.tsx @@ -1,179 +1,181 @@ -'use client'; -import type { TOCItemType } from 'fumadocs-core/server'; -import * as Primitive from 'fumadocs-core/toc'; -import { type ComponentProps, useEffect, useRef, useState } from 'react'; -import { cn } from '../../lib/cn'; -import { TocThumb } from './toc-thumb'; -import { useTOCItems } from './toc'; -import { mergeRefs } from '../../lib/merge-refs'; -import { useI18n } from 'fumadocs-ui/contexts/i18n'; +"use client"; +import type { TOCItemType } from "fumadocs-core/server"; +import * as Primitive from "fumadocs-core/toc"; +import { useI18n } from "fumadocs-ui/contexts/i18n"; +import { type ComponentProps, useEffect, useRef, useState } from "react"; +import { cn } from "../../lib/cn"; +import { mergeRefs } from "../../lib/merge-refs"; +import { useTOCItems } from "./toc"; +import { TocThumb } from "./toc-thumb"; export default function ClerkTOCItems({ - ref, - className, - ...props -}: ComponentProps<'div'>) { - const containerRef = useRef(null); - const items = useTOCItems(); - const { text } = useI18n(); - - const [svg, setSvg] = useState<{ - path: string; - width: number; - height: number; - }>(); - - useEffect(() => { - if (!containerRef.current) return; - const container = containerRef.current; - - function onResize(): void { - if (container.clientHeight === 0) return; - let w = 0, - h = 0; - const d: string[] = []; - for (let i = 0; i < items.length; i++) { - const element: HTMLElement | null = container.querySelector( - `a[href="#${items[i].url.slice(1)}"]`, - ); - if (!element) continue; - - const styles = getComputedStyle(element); - const offset = getLineOffset(items[i].depth) + 1, - top = element.offsetTop + parseFloat(styles.paddingTop), - bottom = - element.offsetTop + - element.clientHeight - - parseFloat(styles.paddingBottom); - - w = Math.max(offset, w); - h = Math.max(h, bottom); - - d.push(`${i === 0 ? 'M' : 'L'}${offset} ${top}`); - d.push(`L${offset} ${bottom}`); - } - - setSvg({ - path: d.join(' '), - width: w + 1, - height: h, - }); - } - - const observer = new ResizeObserver(onResize); - onResize(); - - observer.observe(container); - return () => { - observer.disconnect(); - }; - }, [items]); - - if (items.length === 0) - return ( -
- {text.tocNoHeadings} -
- ); - - return ( - <> - {svg ? ( -
`, - ) - }")`, - }} - > - -
- ) : null} -
- {items.map((item, i) => ( - - ))} -
- - ); + ref, + className, + ...props +}: ComponentProps<"div">) { + const containerRef = useRef(null); + const items = useTOCItems(); + const { text } = useI18n(); + + const [svg, setSvg] = useState<{ + path: string; + width: number; + height: number; + }>(); + + useEffect(() => { + if (!containerRef.current) return; + const container = containerRef.current; + + function onResize(): void { + if (container.clientHeight === 0) return; + let w = 0, + h = 0; + const d: string[] = []; + for (let i = 0; i < items.length; i++) { + const element: HTMLElement | null = container.querySelector( + `a[href="#${items[i].url.slice(1)}"]`, + ); + if (!element) continue; + + const styles = getComputedStyle(element); + const offset = getLineOffset(items[i].depth) + 1, + top = element.offsetTop + parseFloat(styles.paddingTop), + bottom = + element.offsetTop + + element.clientHeight - + parseFloat(styles.paddingBottom); + + w = Math.max(offset, w); + h = Math.max(h, bottom); + + d.push(`${i === 0 ? "M" : "L"}${offset} ${top}`); + d.push(`L${offset} ${bottom}`); + } + + setSvg({ + path: d.join(" "), + width: w + 1, + height: h, + }); + } + + const observer = new ResizeObserver(onResize); + onResize(); + + observer.observe(container); + return () => { + observer.disconnect(); + }; + }, [items]); + + if (items.length === 0) + return ( +
+ {text.tocNoHeadings} +
+ ); + + return ( + <> + {svg ? ( +
`, + ) + }")`, + }} + > + +
+ ) : null} +
+ {items.map((item, i) => ( + + ))} +
+ + ); } function getItemOffset(depth: number): number { - if (depth <= 2) return 14; - if (depth === 3) return 26; - return 36; + if (depth <= 2) return 14; + if (depth === 3) return 26; + return 36; } function getLineOffset(depth: number): number { - return depth >= 3 ? 10 : 0; + return depth >= 3 ? 10 : 0; } function TOCItem({ - item, - upper = item.depth, - lower = item.depth, + item, + upper = item.depth, + lower = item.depth, }: { - item: TOCItemType; - upper?: number; - lower?: number; + item: TOCItemType; + upper?: number; + lower?: number; }) { - const offset = getLineOffset(item.depth), - upperOffset = getLineOffset(upper), - lowerOffset = getLineOffset(lower); - - return ( - - {offset !== upperOffset ? ( - - - - ) : null} -
- {item.title} - - ); + const offset = getLineOffset(item.depth), + upperOffset = getLineOffset(upper), + lowerOffset = getLineOffset(lower); + + return ( + + {offset !== upperOffset ? ( + + Hierarchy connector + + + ) : null} +
+ {item.title} + + ); } diff --git a/src/components/ui/toc-thumb.tsx b/src/components/ui/toc-thumb.tsx index bdced31a..d8070283 100644 --- a/src/components/ui/toc-thumb.tsx +++ b/src/components/ui/toc-thumb.tsx @@ -1,73 +1,73 @@ -import { type HTMLAttributes, type RefObject, useEffect, useRef } from 'react'; -import * as Primitive from 'fumadocs-core/toc'; -import { useOnChange } from 'fumadocs-core/utils/use-on-change'; -import { useEffectEvent } from 'fumadocs-core/utils/use-effect-event'; +import * as Primitive from "fumadocs-core/toc"; +import { useEffectEvent } from "fumadocs-core/utils/use-effect-event"; +import { useOnChange } from "fumadocs-core/utils/use-on-change"; +import { type HTMLAttributes, type RefObject, useEffect, useRef } from "react"; export type TOCThumb = [top: number, height: number]; function calc(container: HTMLElement, active: string[]): TOCThumb { - if (active.length === 0 || container.clientHeight === 0) { - return [0, 0]; - } - - let upper = Number.MAX_VALUE, - lower = 0; - - for (const item of active) { - const element = container.querySelector(`a[href="#${item}"]`); - if (!element) continue; - - const styles = getComputedStyle(element); - upper = Math.min(upper, element.offsetTop + parseFloat(styles.paddingTop)); - lower = Math.max( - lower, - element.offsetTop + - element.clientHeight - - parseFloat(styles.paddingBottom), - ); - } - - return [upper, lower - upper]; + if (active.length === 0 || container.clientHeight === 0) { + return [0, 0]; + } + + let upper = Number.MAX_VALUE, + lower = 0; + + for (const item of active) { + const element = container.querySelector(`a[href="#${item}"]`); + if (!element) continue; + + const styles = getComputedStyle(element); + upper = Math.min(upper, element.offsetTop + parseFloat(styles.paddingTop)); + lower = Math.max( + lower, + element.offsetTop + + element.clientHeight - + parseFloat(styles.paddingBottom), + ); + } + + return [upper, lower - upper]; } function update(element: HTMLElement, info: TOCThumb): void { - element.style.setProperty('--fd-top', `${info[0]}px`); - element.style.setProperty('--fd-height', `${info[1]}px`); + element.style.setProperty("--fd-top", `${info[0]}px`); + element.style.setProperty("--fd-height", `${info[1]}px`); } export function TocThumb({ - containerRef, - ...props + containerRef, + ...props }: HTMLAttributes & { - containerRef: RefObject; + containerRef: RefObject; }) { - const active = Primitive.useActiveAnchors(); - const thumbRef = useRef(null); + const active = Primitive.useActiveAnchors(); + const thumbRef = useRef(null); - const onResize = useEffectEvent(() => { - if (!containerRef.current || !thumbRef.current) return; + const onResize = useEffectEvent(() => { + if (!containerRef.current || !thumbRef.current) return; - update(thumbRef.current, calc(containerRef.current, active)); - }); + update(thumbRef.current, calc(containerRef.current, active)); + }); - useEffect(() => { - if (!containerRef.current) return; - const container = containerRef.current; + useEffect(() => { + if (!containerRef.current) return; + const container = containerRef.current; - onResize(); - const observer = new ResizeObserver(onResize); - observer.observe(container); + onResize(); + const observer = new ResizeObserver(onResize); + observer.observe(container); - return () => { - observer.disconnect(); - }; - }, [containerRef, onResize]); + return () => { + observer.disconnect(); + }; + }, [containerRef, onResize]); - useOnChange(active, () => { - if (!containerRef.current || !thumbRef.current) return; + useOnChange(active, () => { + if (!containerRef.current || !thumbRef.current) return; - update(thumbRef.current, calc(containerRef.current, active)); - }); + update(thumbRef.current, calc(containerRef.current, active)); + }); - return
; + return
; } diff --git a/src/components/ui/toc.tsx b/src/components/ui/toc.tsx index 81cebc2d..e8248182 100644 --- a/src/components/ui/toc.tsx +++ b/src/components/ui/toc.tsx @@ -1,101 +1,101 @@ -'use client'; -import type { TOCItemType } from 'fumadocs-core/server'; -import * as Primitive from 'fumadocs-core/toc'; -import { type ComponentProps, createContext, useContext, useRef } from 'react'; -import { cn } from '../../lib/cn'; -import { useI18n } from 'fumadocs-ui/contexts/i18n'; -import { TocThumb } from './toc-thumb'; -import { mergeRefs } from '../../lib/merge-refs'; +"use client"; +import type { TOCItemType } from "fumadocs-core/server"; +import * as Primitive from "fumadocs-core/toc"; +import { useI18n } from "fumadocs-ui/contexts/i18n"; +import { type ComponentProps, createContext, useContext, useRef } from "react"; +import { cn } from "../../lib/cn"; +import { mergeRefs } from "../../lib/merge-refs"; +import { TocThumb } from "./toc-thumb"; const TOCContext = createContext([]); export function useTOCItems(): TOCItemType[] { - return useContext(TOCContext); + return useContext(TOCContext); } export function TOCProvider({ - toc, - children, - ...props + toc, + children, + ...props }: ComponentProps) { - return ( - - - {children} - - - ); + return ( + + + {children} + + + ); } export function TOCScrollArea({ - ref, - className, - ...props -}: ComponentProps<'div'>) { - const viewRef = useRef(null); + ref, + className, + ...props +}: ComponentProps<"div">) { + const viewRef = useRef(null); - return ( -
- - {props.children} - -
- ); + return ( +
+ + {props.children} + +
+ ); } -export function TOCItems({ ref, className, ...props }: ComponentProps<'div'>) { - const containerRef = useRef(null); - const items = useTOCItems(); - const { text } = useI18n(); +export function TOCItems({ ref, className, ...props }: ComponentProps<"div">) { + const containerRef = useRef(null); + const items = useTOCItems(); + const { text } = useI18n(); - if (items.length === 0) - return ( -
- {text.tocNoHeadings} -
- ); + if (items.length === 0) + return ( +
+ {text.tocNoHeadings} +
+ ); - return ( - <> - -
- {items.map((item) => ( - - ))} -
- - ); + return ( + <> + +
+ {items.map((item) => ( + + ))} +
+ + ); } function TOCItem({ item }: { item: TOCItemType }) { - return ( - = 4 && 'ps-8', - )} - > - {item.title} - - ); + return ( + = 4 && "ps-8", + )} + > + {item.title} + + ); } diff --git a/src/lib/cn.ts b/src/lib/cn.ts index ba66fd25..8e473dac 100644 --- a/src/lib/cn.ts +++ b/src/lib/cn.ts @@ -1 +1 @@ -export { twMerge as cn } from 'tailwind-merge'; +export { twMerge as cn } from "tailwind-merge"; diff --git a/src/lib/is-active.ts b/src/lib/is-active.ts index d41bf698..53cadd77 100644 --- a/src/lib/is-active.ts +++ b/src/lib/is-active.ts @@ -1,23 +1,23 @@ -import type { SidebarTab } from 'fumadocs-ui/utils/get-sidebar-tabs'; +import type { SidebarTab } from "fumadocs-ui/utils/get-sidebar-tabs"; function normalize(url: string) { - if (url.length > 1 && url.endsWith('/')) return url.slice(0, -1); - return url; + if (url.length > 1 && url.endsWith("/")) return url.slice(0, -1); + return url; } export function isActive( - url: string, - pathname: string, - nested = true, + url: string, + pathname: string, + nested = true, ): boolean { - url = normalize(url); - pathname = normalize(pathname); + url = normalize(url); + pathname = normalize(pathname); - return url === pathname || (nested && pathname.startsWith(`${url}/`)); + return url === pathname || (nested && pathname.startsWith(`${url}/`)); } export function isTabActive(tab: SidebarTab, pathname: string) { - if (tab.urls) return tab.urls.has(normalize(pathname)); + if (tab.urls) return tab.urls.has(normalize(pathname)); - return isActive(tab.url, pathname, true); + return isActive(tab.url, pathname, true); } diff --git a/src/lib/merge-refs.ts b/src/lib/merge-refs.ts index 7d05f74a..3de81d3f 100644 --- a/src/lib/merge-refs.ts +++ b/src/lib/merge-refs.ts @@ -1,15 +1,15 @@ -import type * as React from 'react'; +import type * as React from "react"; export function mergeRefs( - ...refs: (React.Ref | undefined)[] + ...refs: (React.Ref | undefined)[] ): React.RefCallback { - return (value) => { - refs.forEach((ref) => { - if (typeof ref === 'function') { - ref(value); - } else if (ref) { - ref.current = value; - } - }); - }; + return (value) => { + refs.forEach((ref) => { + if (typeof ref === "function") { + ref(value); + } else if (ref) { + ref.current = value; + } + }); + }; } From 86872a96e3e03d4827dc5352fb40e9043b02284c Mon Sep 17 00:00:00 2001 From: Steve Date: Tue, 9 Sep 2025 13:28:35 -0400 Subject: [PATCH 11/14] chore: fixed acorn issue --- content/contracts/v5.x/erc1155.mdx | 4 ++-- content/contracts/v5.x/erc4626.mdx | 6 +++--- content/contracts/v5.x/upgradeable.mdx | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/content/contracts/v5.x/erc1155.mdx b/content/contracts/v5.x/erc1155.mdx index d77f1939..c01f13a7 100644 --- a/content/contracts/v5.x/erc1155.mdx +++ b/content/contracts/v5.x/erc1155.mdx @@ -71,9 +71,9 @@ The metadata uri can be obtained: "https://game.example/api/item/{id}.json" ``` -The `uri` can include the string `+{id}+` which clients must replace with the actual token ID, in lowercase hexadecimal (with no 0x prefix) and leading zero padded to 64 hex characters. +The `uri` can include the string `+id+` which clients must replace with the actual token ID, in lowercase hexadecimal (with no 0x prefix) and leading zero padded to 64 hex characters. -For token ID `2` and uri `+https://game.example/api/item/{id}.json+` clients would replace `+{id}+` with `0000000000000000000000000000000000000000000000000000000000000002` to retrieve JSON at `https://game.example/api/item/0000000000000000000000000000000000000000000000000000000000000002.json`. +For token ID `2` and uri `+https://game.example/api/item/id.json+` clients would replace `+id+` with `0000000000000000000000000000000000000000000000000000000000000002` to retrieve JSON at `https://game.example/api/item/0000000000000000000000000000000000000000000000000000000000000002.json`. The JSON document for token ID 2 might look something like: diff --git a/content/contracts/v5.x/erc4626.mdx b/content/contracts/v5.x/erc4626.mdx index 262fbcfe..bb6bed6d 100644 --- a/content/contracts/v5.x/erc4626.mdx +++ b/content/contracts/v5.x/erc4626.mdx @@ -63,7 +63,7 @@ In math that gives: | $0$ | $0$ | - | after attacker’s deposit | | $a_0$ | $a_0$ | $1$ | after attacker’s donation | -This means a deposit of $u$ will give $\frac{u \times a_0}{a_0 + a_1}$ shares. +This means a deposit of $u$ will give $\fracu \times a_0a_0 + a_1$ shares. For the attacker to dilute that deposit to 0 shares, causing the user to lose all its deposit, it must ensure that @@ -73,7 +73,7 @@ For the attacker to dilute that deposit to 0 shares, causing the user to lose al Using $a_0 = 1$ and $a_1 = u$ is enough. So the attacker only needs $u+1$ assets to perform a successful attack. -It is easy to generalize the above results to scenarios where the attacker is going after a smaller fraction of the user’s deposit. In order to target $\frac{u}{n}$, the user needs to suffer rounding of a similar fraction, which means the user must receive at most $n$ shares. This results in: +It is easy to generalize the above results to scenarios where the attacker is going after a smaller fraction of the user’s deposit. In order to target $\fracun$, the user needs to suffer rounding of a similar fraction, which means the user must receive at most $n$ shares. This results in: ```math \frac{u \times a_0}{a_0+a_1} < n \iff \frac{u}{n} < 1 + \frac{a_1}{a_0} @@ -103,7 +103,7 @@ Following the previous math definitions, we have: | after attacker's deposit | $1+a_0$ | $10^\delta \times (1+a_0)$ | $10^\delta$ | | after attacker's donation | $1+a_0+a_1$ | $10^\delta \times (1+a_0)$ | $10^\delta$ | -One important thing to note is that the attacker only owns a fraction $\frac{a_0}{1 + a_0}$ of the shares, so when doing the donation, he will only be able to recover that fraction $\frac{a_1 \times a_0}{1 + a_0}$ of the donation. The remaining $\frac{a_1}{1+a_0}$ are captured by the vault. +One important thing to note is that the attacker only owns a fraction $\fraca_01 + a_0$ of the shares, so when doing the donation, he will only be able to recover that fraction $\fraca_1 \times a_01 + a_0$ of the donation. The remaining $\fraca_11+a_0$ are captured by the vault. ```math \mathit{loss} = \frac{a_1}{1+a_0} diff --git a/content/contracts/v5.x/upgradeable.mdx b/content/contracts/v5.x/upgradeable.mdx index 2c7ad167..2540687e 100644 --- a/content/contracts/v5.x/upgradeable.mdx +++ b/content/contracts/v5.x/upgradeable.mdx @@ -36,7 +36,7 @@ The Upgradeable package replicates the structure of the main OpenZeppelin Contra Interfaces and libraries are not included in the Upgradeable package, but are instead imported from the main OpenZeppelin Contracts package. -Constructors are replaced by internal initializer functions following the naming convention `__{ContractName}_init`. Since these are internal, you must always define your own public initializer function and call the parent initializer of the contract you extend. +Constructors are replaced by internal initializer functions following the naming convention `__ContractName_init`. Since these are internal, you must always define your own public initializer function and call the parent initializer of the contract you extend. ```diff - constructor() ERC721("MyCollectible", "MCO") public { @@ -69,9 +69,9 @@ main(); ### Multiple Inheritance -Initializer functions are not linearized by the compiler like constructors. Because of this, each `__{ContractName}_init` function embeds the linearized calls to all parent initializers. As a consequence, calling two of these `init` functions can potentially initialize the same contract twice. +Initializer functions are not linearized by the compiler like constructors. Because of this, each `__ContractName_init` function embeds the linearized calls to all parent initializers. As a consequence, calling two of these `init` functions can potentially initialize the same contract twice. -The function `__{ContractName}_init_unchained` found in every contract is the initializer function minus the calls to parent initializers, and can be used to avoid the double initialization problem, but doing this manually is not recommended. We hope to be able to implement safety checks for this in future versions of the Upgrades Plugins. +The function `__ContractName_init_unchained` found in every contract is the initializer function minus the calls to parent initializers, and can be used to avoid the double initialization problem, but doing this manually is not recommended. We hope to be able to implement safety checks for this in future versions of the Upgrades Plugins. ### Namespaced Storage From 97f16877cc28cf82e91c87b37c9098e333397349 Mon Sep 17 00:00:00 2001 From: Steve Date: Tue, 9 Sep 2025 13:59:21 -0400 Subject: [PATCH 12/14] chore: updated openapi approach --- .../schemas/openzeppelin-relayer-openapi.json | 7416 +++++++++++++++++ src/lib/openapi.ts | 5 +- 2 files changed, 7417 insertions(+), 4 deletions(-) create mode 100644 public/schemas/openzeppelin-relayer-openapi.json diff --git a/public/schemas/openzeppelin-relayer-openapi.json b/public/schemas/openzeppelin-relayer-openapi.json new file mode 100644 index 00000000..3f2aac91 --- /dev/null +++ b/public/schemas/openzeppelin-relayer-openapi.json @@ -0,0 +1,7416 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "OpenZeppelin Relayer API", + "description": "OpenZeppelin Relayer API", + "termsOfService": "https://www.openzeppelin.com/tos", + "contact": { + "name": "OpenZeppelin", + "url": "https://www.openzeppelin.com" + }, + "license": { + "name": "AGPL-3.0 license", + "url": "https://github.com/OpenZeppelin/openzeppelin-relayer/blob/main/LICENSE" + }, + "version": "1.0.0" + }, + "paths": { + "/api/v1/notifications": { + "get": { + "tags": [ + "Notifications" + ], + "summary": "Notification routes implementation", + "description": "Note: OpenAPI documentation for these endpoints can be found in the `openapi.rs` file\n\nLists all notifications with pagination support.", + "operationId": "listNotifications", + "parameters": [ + { + "name": "page", + "in": "query", + "description": "Page number for pagination (starts at 1)", + "required": false, + "schema": { + "type": "integer", + "minimum": 0 + } + }, + { + "name": "per_page", + "in": "query", + "description": "Number of items per page (default: 10)", + "required": false, + "schema": { + "type": "integer", + "minimum": 0 + } + } + ], + "responses": { + "200": { + "description": "Notification list retrieved successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_Vec_NotificationResponse" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Bad Request", + "success": false + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Unauthorized", + "success": false + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Internal Server Error", + "success": false + } + } + } + } + }, + "security": [ + { + "bearer_auth": [] + } + ] + }, + "post": { + "tags": [ + "Notifications" + ], + "summary": "Creates a new notification.", + "operationId": "createNotification", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotificationCreateRequest" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Notification created successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_NotificationResponse" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Bad Request", + "success": false + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Unauthorized", + "success": false + } + } + } + }, + "409": { + "description": "Notification with this ID already exists", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Notification with this ID already exists", + "success": false + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Internal Server Error", + "success": false + } + } + } + } + }, + "security": [ + { + "bearer_auth": [] + } + ] + } + }, + "/api/v1/notifications/{notification_id}": { + "get": { + "tags": [ + "Notifications" + ], + "summary": "Retrieves details of a specific notification by ID.", + "operationId": "getNotification", + "parameters": [ + { + "name": "notification_id", + "in": "path", + "description": "Notification ID", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Notification retrieved successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_NotificationResponse" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Bad Request", + "success": false + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Unauthorized", + "success": false + } + } + } + }, + "404": { + "description": "Notification not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Notification not found", + "success": false + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Internal Server Error", + "success": false + } + } + } + } + }, + "security": [ + { + "bearer_auth": [] + } + ] + }, + "delete": { + "tags": [ + "Notifications" + ], + "summary": "Deletes a notification by ID.", + "operationId": "deleteNotification", + "parameters": [ + { + "name": "notification_id", + "in": "path", + "description": "Notification ID", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Notification deleted successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": "Notification deleted successfully", + "message": "Notification deleted successfully", + "success": true + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Bad Request", + "success": false + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Unauthorized", + "success": false + } + } + } + }, + "404": { + "description": "Notification not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Notification not found", + "success": false + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Internal Server Error", + "success": false + } + } + } + } + }, + "security": [ + { + "bearer_auth": [] + } + ] + }, + "patch": { + "tags": [ + "Notifications" + ], + "summary": "Updates an existing notification.", + "operationId": "updateNotification", + "parameters": [ + { + "name": "notification_id", + "in": "path", + "description": "Notification ID", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotificationUpdateRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Notification updated successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_NotificationResponse" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Bad Request", + "success": false + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Unauthorized", + "success": false + } + } + } + }, + "404": { + "description": "Notification not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Notification not found", + "success": false + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Internal Server Error", + "success": false + } + } + } + } + }, + "security": [ + { + "bearer_auth": [] + } + ] + } + }, + "/api/v1/plugins/{plugin_id}/call": { + "post": { + "tags": [ + "Plugins" + ], + "summary": "Calls a plugin method.", + "operationId": "callPlugin", + "parameters": [ + { + "name": "plugin_id", + "in": "path", + "description": "The unique identifier of the plugin", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PluginCallRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Plugin call successful", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_PluginCallResponse" + } + } + } + }, + "400": { + "description": "BadRequest", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Bad Request", + "success": false + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Unauthorized", + "success": false + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Plugin with ID plugin_id not found", + "success": false + } + } + } + }, + "429": { + "description": "Too Many Requests", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Too Many Requests", + "success": false + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Internal Server Error", + "success": false + } + } + } + } + }, + "security": [ + { + "bearer_auth": [] + } + ] + } + }, + "/api/v1/relayers": { + "get": { + "tags": [ + "Relayers" + ], + "summary": "Relayer routes implementation", + "description": "Note: OpenAPI documentation for these endpoints can be found in the `openapi.rs` file\n\nLists all relayers with pagination support.", + "operationId": "listRelayers", + "parameters": [ + { + "name": "page", + "in": "query", + "description": "Page number for pagination (starts at 1)", + "required": false, + "schema": { + "type": "integer", + "minimum": 0 + } + }, + { + "name": "per_page", + "in": "query", + "description": "Number of items per page (default: 10)", + "required": false, + "schema": { + "type": "integer", + "minimum": 0 + } + } + ], + "responses": { + "200": { + "description": "Relayer list retrieved successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_Vec_RelayerResponse" + } + } + } + }, + "400": { + "description": "BadRequest", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Bad Request", + "success": false + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Unauthorized", + "success": false + } + } + } + }, + "429": { + "description": "Too Many Requests", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Too Many Requests", + "success": false + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Internal Server Error", + "success": false + } + } + } + } + }, + "security": [ + { + "bearer_auth": [] + } + ] + }, + "post": { + "tags": [ + "Relayers" + ], + "summary": "Creates a new relayer.", + "operationId": "createRelayer", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateRelayerRequest" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Relayer created successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_RelayerResponse" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Bad Request", + "success": false + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Unauthorized", + "success": false + } + } + } + }, + "409": { + "description": "Relayer with this ID already exists", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Relayer with this ID already exists", + "success": false + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Internal Server Error", + "success": false + } + } + } + } + }, + "security": [ + { + "bearer_auth": [] + } + ] + } + }, + "/api/v1/relayers/{relayer_id}": { + "get": { + "tags": [ + "Relayers" + ], + "summary": "Retrieves details of a specific relayer by ID.", + "operationId": "getRelayer", + "parameters": [ + { + "name": "relayer_id", + "in": "path", + "description": "The unique identifier of the relayer", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Relayer details retrieved successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_RelayerResponse" + } + } + } + }, + "400": { + "description": "BadRequest", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Bad Request", + "success": false + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Unauthorized", + "success": false + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Relayer with ID relayer_id not found", + "success": false + } + } + } + }, + "429": { + "description": "Too Many Requests", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Too Many Requests", + "success": false + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Internal Server Error", + "success": false + } + } + } + } + }, + "security": [ + { + "bearer_auth": [] + } + ] + }, + "delete": { + "tags": [ + "Relayers" + ], + "summary": "Deletes a relayer by ID.", + "operationId": "deleteRelayer", + "parameters": [ + { + "name": "relayer_id", + "in": "path", + "description": "The unique identifier of the relayer", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Relayer deleted successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + } + } + } + }, + "400": { + "description": "Bad Request - Cannot delete relayer with active transactions", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Cannot delete relayer 'relayer_id' because it has N transaction(s). Please wait for all transactions to complete or cancel them before deleting the relayer.", + "success": false + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Unauthorized", + "success": false + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Relayer with ID relayer_id not found", + "success": false + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Internal Server Error", + "success": false + } + } + } + } + }, + "security": [ + { + "bearer_auth": [] + } + ] + }, + "patch": { + "tags": [ + "Relayers" + ], + "summary": "Updates a relayer's information based on the provided update request.", + "operationId": "updateRelayer", + "parameters": [ + { + "name": "relayer_id", + "in": "path", + "description": "The unique identifier of the relayer", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateRelayerRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Relayer updated successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_RelayerResponse" + } + } + } + }, + "400": { + "description": "BadRequest", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Bad Request", + "success": false + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Unauthorized", + "success": false + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Relayer with ID relayer_id not found", + "success": false + } + } + } + }, + "429": { + "description": "Too Many Requests", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Too Many Requests", + "success": false + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Internal Server Error", + "success": false + } + } + } + } + }, + "security": [ + { + "bearer_auth": [] + } + ] + } + }, + "/api/v1/relayers/{relayer_id}/balance": { + "get": { + "tags": [ + "Relayers" + ], + "summary": "Retrieves the balance of a specific relayer.", + "operationId": "getRelayerBalance", + "parameters": [ + { + "name": "relayer_id", + "in": "path", + "description": "The unique identifier of the relayer", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Relayer balance retrieved successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_BalanceResponse" + } + } + } + }, + "400": { + "description": "BadRequest", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Bad Request", + "success": false + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Unauthorized", + "success": false + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Relayer with ID relayer_id not found", + "success": false + } + } + } + }, + "429": { + "description": "Too Many Requests", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Too Many Requests", + "success": false + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Internal Server Error", + "success": false + } + } + } + } + }, + "security": [ + { + "bearer_auth": [] + } + ] + } + }, + "/api/v1/relayers/{relayer_id}/rpc": { + "post": { + "tags": [ + "Relayers" + ], + "summary": "Performs a JSON-RPC call using the specified relayer.", + "operationId": "rpc", + "parameters": [ + { + "name": "relayer_id", + "in": "path", + "description": "The unique identifier of the relayer", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "JSON-RPC request with method and parameters", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/JsonRpcRequest_NetworkRpcRequest" + }, + "example": { + "id": 1, + "jsonrpc": "2.0", + "method": "feeEstimate", + "params": { + "fee_token": "SOL", + "network": "solana", + "transaction": "base64_encoded_transaction" + } + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "RPC method executed successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/JsonRpcResponse_NetworkRpcResult" + } + } + } + }, + "400": { + "description": "BadRequest", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Bad Request", + "success": false + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Unauthorized", + "success": false + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Relayer with ID relayer_id not found", + "success": false + } + } + } + }, + "429": { + "description": "Too Many Requests", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Too Many Requests", + "success": false + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Internal Server Error", + "success": false + } + } + } + } + }, + "security": [ + { + "bearer_auth": [] + } + ] + } + }, + "/api/v1/relayers/{relayer_id}/sign": { + "post": { + "tags": [ + "Relayers" + ], + "summary": "Signs data using the specified relayer.", + "operationId": "sign", + "parameters": [ + { + "name": "relayer_id", + "in": "path", + "description": "The unique identifier of the relayer", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SignDataRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Relayer signed data successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_SignDataResponse" + } + } + } + }, + "400": { + "description": "BadRequest", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Bad Request", + "success": false + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Unauthorized", + "success": false + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Not found", + "success": false + } + } + } + }, + "429": { + "description": "Too Many Requests", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Too Many Requests", + "success": false + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Internal Server Error", + "success": false + } + } + } + } + }, + "security": [ + { + "bearer_auth": [] + } + ] + } + }, + "/api/v1/relayers/{relayer_id}/sign-transaction": { + "post": { + "tags": [ + "Relayers" + ], + "summary": "Signs a transaction using the specified relayer (Stellar only).", + "operationId": "signTransaction", + "parameters": [ + { + "name": "relayer_id", + "in": "path", + "description": "The unique identifier of the relayer", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SignTransactionRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Transaction signed successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_SignTransactionResponse" + } + } + } + }, + "400": { + "description": "BadRequest", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Bad Request", + "success": false + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Unauthorized", + "success": false + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Relayer with ID relayer_id not found", + "success": false + } + } + } + }, + "429": { + "description": "Too Many Requests", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Too Many Requests", + "success": false + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Internal Server Error", + "success": false + } + } + } + } + }, + "security": [ + { + "bearer_auth": [] + } + ] + } + }, + "/api/v1/relayers/{relayer_id}/sign-typed-data": { + "post": { + "tags": [ + "Relayers" + ], + "summary": "Signs typed data using the specified relayer.", + "operationId": "signTypedData", + "parameters": [ + { + "name": "relayer_id", + "in": "path", + "description": "The unique identifier of the relayer", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SignTypedDataRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Relayer signed typed data successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_SignDataResponse" + } + } + } + }, + "400": { + "description": "BadRequest", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Bad Request", + "success": false + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Unauthorized", + "success": false + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Relayer with ID relayer_id not found", + "success": false + } + } + } + }, + "429": { + "description": "Too Many Requests", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Too Many Requests", + "success": false + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Internal Server Error", + "success": false + } + } + } + } + }, + "security": [ + { + "bearer_auth": [] + } + ] + } + }, + "/api/v1/relayers/{relayer_id}/status": { + "get": { + "tags": [ + "Relayers" + ], + "summary": "Fetches the current status of a specific relayer.", + "operationId": "getRelayerStatus", + "parameters": [ + { + "name": "relayer_id", + "in": "path", + "description": "The unique identifier of the relayer", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Relayer status retrieved successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_RelayerStatus" + } + } + } + }, + "400": { + "description": "BadRequest", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Bad Request", + "success": false + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Unauthorized", + "success": false + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Relayer with ID relayer_id not found", + "success": false + } + } + } + }, + "429": { + "description": "Too Many Requests", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Too Many Requests", + "success": false + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Internal Server Error", + "success": false + } + } + } + } + }, + "security": [ + { + "bearer_auth": [] + } + ] + } + }, + "/api/v1/relayers/{relayer_id}/transactions": { + "post": { + "tags": [ + "Relayers" + ], + "summary": "Sends a transaction through the specified relayer.", + "operationId": "sendTransaction", + "parameters": [ + { + "name": "relayer_id", + "in": "path", + "description": "The unique identifier of the relayer", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NetworkTransactionRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Relayer transactions sent successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_TransactionResponse" + } + } + } + }, + "400": { + "description": "BadRequest", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Bad Request", + "success": false + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Unauthorized", + "success": false + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Relayer with ID relayer_id not found", + "success": false + } + } + } + }, + "429": { + "description": "Too Many Requests", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Too Many Requests", + "success": false + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Internal Server Error", + "success": false + } + } + } + } + }, + "security": [ + { + "bearer_auth": [] + } + ] + } + }, + "/api/v1/relayers/{relayer_id}/transactions/": { + "get": { + "tags": [ + "Relayers" + ], + "summary": "Lists all transactions for a specific relayer with pagination.", + "operationId": "listTransactions", + "parameters": [ + { + "name": "relayer_id", + "in": "path", + "description": "The unique identifier of the relayer", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "page", + "in": "query", + "description": "Page number for pagination (starts at 1)", + "required": false, + "schema": { + "type": "integer", + "minimum": 0 + } + }, + { + "name": "per_page", + "in": "query", + "description": "Number of items per page (default: 10)", + "required": false, + "schema": { + "type": "integer", + "minimum": 0 + } + } + ], + "responses": { + "200": { + "description": "Relayer transactions retrieved successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_Vec_TransactionResponse" + } + } + } + }, + "400": { + "description": "BadRequest", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Bad Request", + "success": false + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Unauthorized", + "success": false + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Relayer with ID relayer_id not found", + "success": false + } + } + } + }, + "429": { + "description": "Too Many Requests", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Too Many Requests", + "success": false + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Internal Server Error", + "success": false + } + } + } + } + }, + "security": [ + { + "bearer_auth": [] + } + ] + } + }, + "/api/v1/relayers/{relayer_id}/transactions/by-nonce/{nonce}": { + "get": { + "tags": [ + "Relayers" + ], + "summary": "Retrieves a transaction by its nonce value.", + "operationId": "getTransactionByNonce", + "parameters": [ + { + "name": "relayer_id", + "in": "path", + "description": "The unique identifier of the relayer", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "nonce", + "in": "path", + "description": "The nonce of the transaction", + "required": true, + "schema": { + "type": "integer", + "minimum": 0 + } + } + ], + "responses": { + "200": { + "description": "Relayer transaction retrieved successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_TransactionResponse" + } + } + } + }, + "400": { + "description": "BadRequest", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Bad Request", + "success": false + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Unauthorized", + "success": false + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Not found", + "success": false + } + } + } + }, + "429": { + "description": "Too Many Requests", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Too Many Requests", + "success": false + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Internal Server Error", + "success": false + } + } + } + } + }, + "security": [ + { + "bearer_auth": [] + } + ] + } + }, + "/api/v1/relayers/{relayer_id}/transactions/pending": { + "delete": { + "tags": [ + "Relayers" + ], + "summary": "Deletes all pending transactions for a specific relayer.", + "operationId": "deletePendingTransactions", + "parameters": [ + { + "name": "relayer_id", + "in": "path", + "description": "The unique identifier of the relayer", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Relayer pending transactions successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_DeletePendingTransactionsResponse" + } + } + } + }, + "400": { + "description": "BadRequest", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Bad Request", + "success": false + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Unauthorized", + "success": false + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Relayer with ID relayer_id not found", + "success": false + } + } + } + }, + "429": { + "description": "Too Many Requests", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Too Many Requests", + "success": false + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Internal Server Error", + "success": false + } + } + } + } + }, + "security": [ + { + "bearer_auth": [] + } + ] + } + }, + "/api/v1/relayers/{relayer_id}/transactions/{transaction_id}": { + "get": { + "tags": [ + "Relayers" + ], + "summary": "Retrieves a specific transaction by its ID.", + "operationId": "getTransactionById", + "parameters": [ + { + "name": "relayer_id", + "in": "path", + "description": "The unique identifier of the relayer", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "transaction_id", + "in": "path", + "description": "The unique identifier of the transaction", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Relayer transaction retrieved successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_TransactionResponse" + } + } + } + }, + "400": { + "description": "BadRequest", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Bad Request", + "success": false + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Unauthorized", + "success": false + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Not Found", + "success": false + } + } + } + }, + "429": { + "description": "Too Many Requests", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Too Many Requests", + "success": false + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Internal Server Error", + "success": false + } + } + } + } + }, + "security": [ + { + "bearer_auth": [] + } + ] + }, + "put": { + "tags": [ + "Relayers" + ], + "summary": "Replaces a specific transaction with a new one.", + "operationId": "replaceTransaction", + "parameters": [ + { + "name": "relayer_id", + "in": "path", + "description": "The unique identifier of the relayer", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "transaction_id", + "in": "path", + "description": "The unique identifier of the transaction", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NetworkTransactionRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Relayer transaction replaced successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_TransactionResponse" + } + } + } + }, + "400": { + "description": "BadRequest", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Bad Request", + "success": false + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Unauthorized", + "success": false + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Not found", + "success": false + } + } + } + }, + "429": { + "description": "Too Many Requests", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Too Many Requests", + "success": false + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Internal Server Error", + "success": false + } + } + } + } + }, + "security": [ + { + "bearer_auth": [] + } + ] + }, + "delete": { + "tags": [ + "Relayers" + ], + "summary": "Cancels a specific transaction by its ID.", + "operationId": "cancelTransaction", + "parameters": [ + { + "name": "relayer_id", + "in": "path", + "description": "The unique identifier of the relayer", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "transaction_id", + "in": "path", + "description": "The unique identifier of the transaction", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Relayer transaction canceled successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_TransactionResponse" + } + } + } + }, + "400": { + "description": "BadRequest", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Bad Request", + "success": false + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Unauthorized", + "success": false + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Not found", + "success": false + } + } + } + }, + "429": { + "description": "Too Many Requests", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Too Many Requests", + "success": false + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Internal Server Error", + "success": false + } + } + } + } + }, + "security": [ + { + "bearer_auth": [] + } + ] + } + }, + "/api/v1/signers": { + "get": { + "tags": [ + "Signers" + ], + "summary": "Signer routes implementation", + "description": "Note: OpenAPI documentation for these endpoints can be found in the `openapi.rs` file\n\nLists all signers with pagination support.", + "operationId": "listSigners", + "parameters": [ + { + "name": "page", + "in": "query", + "description": "Page number for pagination (starts at 1)", + "required": false, + "schema": { + "type": "integer", + "minimum": 0 + } + }, + { + "name": "per_page", + "in": "query", + "description": "Number of items per page (default: 10)", + "required": false, + "schema": { + "type": "integer", + "minimum": 0 + } + } + ], + "responses": { + "200": { + "description": "Signer list retrieved successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_Vec_SignerResponse" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Bad Request", + "success": false + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Unauthorized", + "success": false + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Internal Server Error", + "success": false + } + } + } + } + }, + "security": [ + { + "bearer_auth": [] + } + ] + }, + "post": { + "tags": [ + "Signers" + ], + "summary": "Creates a new signer.", + "operationId": "createSigner", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SignerCreateRequest" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Signer created successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_SignerResponse" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Bad Request", + "success": false + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Unauthorized", + "success": false + } + } + } + }, + "409": { + "description": "Signer with this ID already exists", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Signer with this ID already exists", + "success": false + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Internal Server Error", + "success": false + } + } + } + } + }, + "security": [ + { + "bearer_auth": [] + } + ] + } + }, + "/api/v1/signers/{signer_id}": { + "get": { + "tags": [ + "Signers" + ], + "summary": "Retrieves details of a specific signer by ID.", + "operationId": "getSigner", + "parameters": [ + { + "name": "signer_id", + "in": "path", + "description": "Signer ID", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Signer retrieved successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_SignerResponse" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Bad Request", + "success": false + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Unauthorized", + "success": false + } + } + } + }, + "404": { + "description": "Signer not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Signer not found", + "success": false + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Internal Server Error", + "success": false + } + } + } + } + }, + "security": [ + { + "bearer_auth": [] + } + ] + }, + "delete": { + "tags": [ + "Signers" + ], + "summary": "Deletes a signer by ID.", + "operationId": "deleteSigner", + "parameters": [ + { + "name": "signer_id", + "in": "path", + "description": "Signer ID", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Signer deleted successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": "Signer deleted successfully", + "message": "Signer deleted successfully", + "success": true + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Bad Request", + "success": false + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Unauthorized", + "success": false + } + } + } + }, + "404": { + "description": "Signer not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Signer not found", + "success": false + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Internal Server Error", + "success": false + } + } + } + } + }, + "security": [ + { + "bearer_auth": [] + } + ] + }, + "patch": { + "tags": [ + "Signers" + ], + "summary": "Updates an existing signer.", + "operationId": "updateSigner", + "parameters": [ + { + "name": "signer_id", + "in": "path", + "description": "Signer ID", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SignerUpdateRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Signer updated successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_SignerResponse" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Bad Request", + "success": false + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Unauthorized", + "success": false + } + } + } + }, + "404": { + "description": "Signer not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Signer not found", + "success": false + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse_String" + }, + "example": { + "data": null, + "message": "Internal Server Error", + "success": false + } + } + } + } + }, + "security": [ + { + "bearer_auth": [] + } + ] + } + }, + "/debug/metrics/scrape": { + "get": { + "tags": [ + "Metrics" + ], + "summary": "Triggers an update of system metrics and returns the result in plain text format.", + "description": "# Returns\n\nAn `HttpResponse` containing the updated metrics in plain text, or an error message if the\nupdate fails.", + "operationId": "scrape_metrics", + "responses": { + "200": { + "description": "Complete metrics in Prometheus exposition format", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + }, + "401": { + "description": "Unauthorized" + } + } + } + }, + "/metrics": { + "get": { + "tags": [ + "Metrics" + ], + "summary": "Metrics routes implementation", + "description": "Note: OpenAPI documentation for these endpoints can be found in the `openapi.rs` file\nReturns a list of all available metric names in JSON format.\n\n# Returns\n\nAn `HttpResponse` containing a JSON array of metric names.", + "operationId": "list_metrics", + "responses": { + "200": { + "description": "Metric names list", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "401": { + "description": "Unauthorized" + } + } + } + }, + "/metrics/{metric_name}": { + "get": { + "tags": [ + "Metrics" + ], + "summary": "Returns the details of a specific metric in plain text format.", + "description": "# Parameters\n\n- `path`: The name of the metric to retrieve details for.\n\n# Returns\n\nAn `HttpResponse` containing the metric details in plain text, or a 404 error if the metric is\nnot found.", + "operationId": "metric_detail", + "parameters": [ + { + "name": "metric_name", + "in": "path", + "description": "Name of the metric to retrieve, e.g. utopia_transactions_total", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Metric details in Prometheus text format", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + }, + "401": { + "description": "Unauthorized - missing or invalid API key" + }, + "403": { + "description": "Forbidden - insufficient permissions to access this metric" + }, + "404": { + "description": "Metric not found" + }, + "429": { + "description": "Too many requests - rate limit for metrics access exceeded" + } + }, + "security": [ + { + "bearer_auth": [ + "metrics:read" + ] + } + ] + } + }, + "/v1/health": { + "get": { + "tags": [ + "Health" + ], + "summary": "Health routes implementation", + "description": "Note: OpenAPI documentation for these endpoints can be found in the `openapi.rs` file\nHandles the `/health` endpoint.\n\nReturns an `HttpResponse` with a status of `200 OK` and a body of `\"OK\"`.", + "operationId": "health", + "responses": { + "200": { + "description": "Service is healthy", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "ApiResponse_BalanceResponse": { + "type": "object", + "required": [ + "success" + ], + "properties": { + "data": { + "type": "object", + "required": [ + "balance", + "unit" + ], + "properties": { + "balance": { + "type": "integer", + "minimum": 0 + }, + "unit": { + "type": "string", + "example": "wei" + } + } + }, + "error": { + "type": "string" + }, + "pagination": { + "$ref": "#/components/schemas/PaginationMeta" + }, + "success": { + "type": "boolean" + } + } + }, + "ApiResponse_DeletePendingTransactionsResponse": { + "type": "object", + "required": [ + "success" + ], + "properties": { + "data": { + "type": "object", + "description": "Response for delete pending transactions operation", + "required": [ + "queued_for_cancellation_transaction_ids", + "failed_to_queue_transaction_ids", + "total_processed" + ], + "properties": { + "failed_to_queue_transaction_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "queued_for_cancellation_transaction_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "total_processed": { + "type": "integer", + "format": "int32", + "minimum": 0 + } + } + }, + "error": { + "type": "string" + }, + "pagination": { + "$ref": "#/components/schemas/PaginationMeta" + }, + "success": { + "type": "boolean" + } + } + }, + "ApiResponse_NotificationResponse": { + "type": "object", + "required": [ + "success" + ], + "properties": { + "data": { + "type": "object", + "description": "Response structure for notification API endpoints", + "required": [ + "id", + "type", + "url", + "has_signing_key" + ], + "properties": { + "has_signing_key": { + "type": "boolean", + "description": "Signing key is hidden in responses for security" + }, + "id": { + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/NotificationType" + }, + "url": { + "type": "string" + } + } + }, + "error": { + "type": "string" + }, + "pagination": { + "$ref": "#/components/schemas/PaginationMeta" + }, + "success": { + "type": "boolean" + } + } + }, + "ApiResponse_PluginCallResponse": { + "type": "object", + "required": [ + "success" + ], + "properties": { + "data": { + "type": "object", + "required": [ + "success", + "return_value", + "message", + "logs", + "error", + "traces" + ], + "properties": { + "error": { + "type": "string" + }, + "logs": { + "type": "array", + "items": { + "$ref": "#/components/schemas/LogEntry" + } + }, + "message": { + "type": "string" + }, + "return_value": { + "type": "string" + }, + "success": { + "type": "boolean" + }, + "traces": { + "type": "array", + "items": {} + } + } + }, + "error": { + "type": "string" + }, + "pagination": { + "$ref": "#/components/schemas/PaginationMeta" + }, + "success": { + "type": "boolean" + } + } + }, + "ApiResponse_RelayerResponse": { + "type": "object", + "required": [ + "success" + ], + "properties": { + "data": { + "type": "object", + "description": "Relayer response model for API endpoints", + "required": [ + "id", + "name", + "network", + "network_type", + "paused", + "signer_id" + ], + "properties": { + "address": { + "type": "string" + }, + "custom_rpc_urls": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RpcConfig" + } + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "network": { + "type": "string" + }, + "network_type": { + "$ref": "#/components/schemas/RelayerNetworkType" + }, + "notification_id": { + "type": "string" + }, + "paused": { + "type": "boolean" + }, + "policies": { + "$ref": "#/components/schemas/RelayerNetworkPolicyResponse", + "description": "Policies without redundant network_type tag - network type is available at top level\nOnly included if user explicitly provided policies (not shown for empty/default policies)" + }, + "signer_id": { + "type": "string" + }, + "system_disabled": { + "type": "boolean" + } + } + }, + "error": { + "type": "string" + }, + "pagination": { + "$ref": "#/components/schemas/PaginationMeta" + }, + "success": { + "type": "boolean" + } + } + }, + "ApiResponse_RelayerStatus": { + "type": "object", + "required": [ + "success" + ], + "properties": { + "data": { + "oneOf": [ + { + "type": "object", + "required": [ + "balance", + "pending_transactions_count", + "system_disabled", + "paused", + "nonce", + "network_type" + ], + "properties": { + "balance": { + "type": "string" + }, + "last_confirmed_transaction_timestamp": { + "type": [ + "string", + "null" + ] + }, + "network_type": { + "type": "string", + "enum": [ + "evm" + ] + }, + "nonce": { + "type": "string" + }, + "paused": { + "type": "boolean" + }, + "pending_transactions_count": { + "type": "integer", + "format": "int64", + "minimum": 0 + }, + "system_disabled": { + "type": "boolean" + } + } + }, + { + "type": "object", + "required": [ + "balance", + "pending_transactions_count", + "system_disabled", + "paused", + "sequence_number", + "network_type" + ], + "properties": { + "balance": { + "type": "string" + }, + "last_confirmed_transaction_timestamp": { + "type": [ + "string", + "null" + ] + }, + "network_type": { + "type": "string", + "enum": [ + "stellar" + ] + }, + "paused": { + "type": "boolean" + }, + "pending_transactions_count": { + "type": "integer", + "format": "int64", + "minimum": 0 + }, + "sequence_number": { + "type": "string" + }, + "system_disabled": { + "type": "boolean" + } + } + }, + { + "type": "object", + "required": [ + "balance", + "pending_transactions_count", + "system_disabled", + "paused", + "network_type" + ], + "properties": { + "balance": { + "type": "string" + }, + "last_confirmed_transaction_timestamp": { + "type": [ + "string", + "null" + ] + }, + "network_type": { + "type": "string", + "enum": [ + "solana" + ] + }, + "paused": { + "type": "boolean" + }, + "pending_transactions_count": { + "type": "integer", + "format": "int64", + "minimum": 0 + }, + "system_disabled": { + "type": "boolean" + } + } + } + ], + "description": "Relayer status with runtime information" + }, + "error": { + "type": "string" + }, + "pagination": { + "$ref": "#/components/schemas/PaginationMeta" + }, + "success": { + "type": "boolean" + } + } + }, + "ApiResponse_SignDataResponse": { + "type": "object", + "required": [ + "success" + ], + "properties": { + "data": { + "oneOf": [ + { + "$ref": "#/components/schemas/SignDataResponseEvm" + }, + { + "$ref": "#/components/schemas/SignDataResponseSolana" + } + ] + }, + "error": { + "type": "string" + }, + "pagination": { + "$ref": "#/components/schemas/PaginationMeta" + }, + "success": { + "type": "boolean" + } + } + }, + "ApiResponse_SignTransactionResponse": { + "type": "object", + "required": [ + "success" + ], + "properties": { + "data": { + "oneOf": [ + { + "$ref": "#/components/schemas/SignTransactionResponseStellar" + }, + { + "type": "array", + "items": { + "type": "integer", + "format": "int32", + "minimum": 0 + } + }, + { + "type": "array", + "items": { + "type": "integer", + "format": "int32", + "minimum": 0 + } + } + ] + }, + "error": { + "type": "string" + }, + "pagination": { + "$ref": "#/components/schemas/PaginationMeta" + }, + "success": { + "type": "boolean" + } + } + }, + "ApiResponse_SignerResponse": { + "type": "object", + "required": [ + "success" + ], + "properties": { + "data": { + "type": "object", + "required": [ + "id", + "type", + "config" + ], + "properties": { + "config": { + "$ref": "#/components/schemas/SignerConfigResponse", + "description": "Non-secret configuration details" + }, + "id": { + "type": "string", + "description": "The unique identifier of the signer" + }, + "type": { + "$ref": "#/components/schemas/SignerType", + "description": "The type of signer (local, aws_kms, google_cloud_kms, vault, etc.)" + } + } + }, + "error": { + "type": "string" + }, + "pagination": { + "$ref": "#/components/schemas/PaginationMeta" + }, + "success": { + "type": "boolean" + } + } + }, + "ApiResponse_String": { + "type": "object", + "required": [ + "success" + ], + "properties": { + "data": { + "type": "string" + }, + "error": { + "type": "string" + }, + "pagination": { + "$ref": "#/components/schemas/PaginationMeta" + }, + "success": { + "type": "boolean" + } + } + }, + "ApiResponse_TransactionResponse": { + "type": "object", + "required": [ + "success" + ], + "properties": { + "data": { + "oneOf": [ + { + "$ref": "#/components/schemas/EvmTransactionResponse" + }, + { + "$ref": "#/components/schemas/SolanaTransactionResponse" + }, + { + "$ref": "#/components/schemas/StellarTransactionResponse" + } + ] + }, + "error": { + "type": "string" + }, + "pagination": { + "$ref": "#/components/schemas/PaginationMeta" + }, + "success": { + "type": "boolean" + } + } + }, + "ApiResponse_Vec_NotificationResponse": { + "type": "object", + "required": [ + "success" + ], + "properties": { + "data": { + "type": "array", + "items": { + "type": "object", + "description": "Response structure for notification API endpoints", + "required": [ + "id", + "type", + "url", + "has_signing_key" + ], + "properties": { + "has_signing_key": { + "type": "boolean", + "description": "Signing key is hidden in responses for security" + }, + "id": { + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/NotificationType" + }, + "url": { + "type": "string" + } + } + } + }, + "error": { + "type": "string" + }, + "pagination": { + "$ref": "#/components/schemas/PaginationMeta" + }, + "success": { + "type": "boolean" + } + } + }, + "ApiResponse_Vec_RelayerResponse": { + "type": "object", + "required": [ + "success" + ], + "properties": { + "data": { + "type": "array", + "items": { + "type": "object", + "description": "Relayer response model for API endpoints", + "required": [ + "id", + "name", + "network", + "network_type", + "paused", + "signer_id" + ], + "properties": { + "address": { + "type": "string" + }, + "custom_rpc_urls": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RpcConfig" + } + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "network": { + "type": "string" + }, + "network_type": { + "$ref": "#/components/schemas/RelayerNetworkType" + }, + "notification_id": { + "type": "string" + }, + "paused": { + "type": "boolean" + }, + "policies": { + "$ref": "#/components/schemas/RelayerNetworkPolicyResponse", + "description": "Policies without redundant network_type tag - network type is available at top level\nOnly included if user explicitly provided policies (not shown for empty/default policies)" + }, + "signer_id": { + "type": "string" + }, + "system_disabled": { + "type": "boolean" + } + } + } + }, + "error": { + "type": "string" + }, + "pagination": { + "$ref": "#/components/schemas/PaginationMeta" + }, + "success": { + "type": "boolean" + } + } + }, + "ApiResponse_Vec_SignerResponse": { + "type": "object", + "required": [ + "success" + ], + "properties": { + "data": { + "type": "array", + "items": { + "type": "object", + "required": [ + "id", + "type", + "config" + ], + "properties": { + "config": { + "$ref": "#/components/schemas/SignerConfigResponse", + "description": "Non-secret configuration details" + }, + "id": { + "type": "string", + "description": "The unique identifier of the signer" + }, + "type": { + "$ref": "#/components/schemas/SignerType", + "description": "The type of signer (local, aws_kms, google_cloud_kms, vault, etc.)" + } + } + } + }, + "error": { + "type": "string" + }, + "pagination": { + "$ref": "#/components/schemas/PaginationMeta" + }, + "success": { + "type": "boolean" + } + } + }, + "ApiResponse_Vec_TransactionResponse": { + "type": "object", + "required": [ + "success" + ], + "properties": { + "data": { + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/EvmTransactionResponse" + }, + { + "$ref": "#/components/schemas/SolanaTransactionResponse" + }, + { + "$ref": "#/components/schemas/StellarTransactionResponse" + } + ] + } + }, + "error": { + "type": "string" + }, + "pagination": { + "$ref": "#/components/schemas/PaginationMeta" + }, + "success": { + "type": "boolean" + } + } + }, + "AssetSpec": { + "oneOf": [ + { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "native" + ] + } + } + }, + { + "type": "object", + "required": [ + "code", + "issuer", + "type" + ], + "properties": { + "code": { + "type": "string" + }, + "issuer": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "credit4" + ] + } + } + }, + { + "type": "object", + "required": [ + "code", + "issuer", + "type" + ], + "properties": { + "code": { + "type": "string" + }, + "issuer": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "credit12" + ] + } + } + } + ] + }, + "AuthSpec": { + "oneOf": [ + { + "type": "object", + "description": "No authorization required", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ] + } + } + }, + { + "type": "object", + "description": "Use the transaction source account for authorization", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "source_account" + ] + } + } + }, + { + "type": "object", + "description": "Use specific addresses for authorization", + "required": [ + "signers", + "type" + ], + "properties": { + "signers": { + "type": "array", + "items": { + "type": "string" + } + }, + "type": { + "type": "string", + "enum": [ + "addresses" + ] + } + } + }, + { + "type": "object", + "description": "Advanced format - provide complete XDR auth entries as base64-encoded strings", + "required": [ + "entries", + "type" + ], + "properties": { + "entries": { + "type": "array", + "items": { + "type": "string" + } + }, + "type": { + "type": "string", + "enum": [ + "xdr" + ] + } + } + } + ], + "description": "Authorization specification for Soroban operations" + }, + "AwsKmsSignerRequestConfig": { + "type": "object", + "description": "AWS KMS signer configuration for API requests", + "required": [ + "region", + "key_id" + ], + "properties": { + "key_id": { + "type": "string" + }, + "region": { + "type": "string" + } + }, + "additionalProperties": false + }, + "BalanceResponse": { + "type": "object", + "required": [ + "balance", + "unit" + ], + "properties": { + "balance": { + "type": "integer", + "minimum": 0 + }, + "unit": { + "type": "string", + "example": "wei" + } + } + }, + "ContractSource": { + "oneOf": [ + { + "type": "object", + "required": [ + "address", + "from" + ], + "properties": { + "address": { + "type": "string" + }, + "from": { + "type": "string", + "enum": [ + "address" + ] + } + } + }, + { + "type": "object", + "required": [ + "contract", + "from" + ], + "properties": { + "contract": { + "type": "string" + }, + "from": { + "type": "string", + "enum": [ + "contract" + ] + } + } + } + ], + "description": "Represents the source for contract creation" + }, + "CreateRelayerPolicyRequest": { + "oneOf": [ + { + "type": "object", + "required": [ + "Evm" + ], + "properties": { + "Evm": { + "$ref": "#/components/schemas/RelayerEvmPolicy" + } + } + }, + { + "type": "object", + "required": [ + "Solana" + ], + "properties": { + "Solana": { + "$ref": "#/components/schemas/RelayerSolanaPolicy" + } + } + }, + { + "type": "object", + "required": [ + "Stellar" + ], + "properties": { + "Stellar": { + "$ref": "#/components/schemas/RelayerStellarPolicy" + } + } + } + ], + "description": "Policy types for create requests - deserialized based on network_type from parent request" + }, + "CreateRelayerRequest": { + "type": "object", + "description": "Request model for creating a new relayer", + "required": [ + "name", + "network", + "paused", + "network_type", + "signer_id" + ], + "properties": { + "custom_rpc_urls": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RpcConfig" + } + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "network": { + "type": "string" + }, + "network_type": { + "$ref": "#/components/schemas/RelayerNetworkType" + }, + "notification_id": { + "type": "string" + }, + "paused": { + "type": "boolean" + }, + "policies": { + "$ref": "#/components/schemas/CreateRelayerPolicyRequest", + "description": "Policies - will be deserialized based on the network_type field" + }, + "signer_id": { + "type": "string" + } + }, + "additionalProperties": false + }, + "DeletePendingTransactionsResponse": { + "type": "object", + "description": "Response for delete pending transactions operation", + "required": [ + "queued_for_cancellation_transaction_ids", + "failed_to_queue_transaction_ids", + "total_processed" + ], + "properties": { + "failed_to_queue_transaction_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "queued_for_cancellation_transaction_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "total_processed": { + "type": "integer", + "format": "int32", + "minimum": 0 + } + } + }, + "EncodedSerializedTransaction": { + "type": "string" + }, + "EvmPolicyResponse": { + "type": "object", + "description": "EVM policy response model for OpenAPI documentation", + "properties": { + "eip1559_pricing": { + "type": "boolean" + }, + "gas_limit_estimation": { + "type": "boolean" + }, + "gas_price_cap": { + "type": "integer", + "minimum": 0 + }, + "min_balance": { + "type": "integer", + "minimum": 0 + }, + "private_transactions": { + "type": "boolean" + }, + "whitelist_receivers": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false + }, + "EvmRpcRequest": { + "oneOf": [ + { + "type": "object", + "required": [ + "method", + "params" + ], + "properties": { + "method": { + "type": "string" + }, + "params": { + "type": "string" + } + } + }, + { + "type": "object", + "required": [ + "method", + "params" + ], + "properties": { + "method": { + "type": "string" + }, + "params": {} + } + } + ] + }, + "EvmRpcResult": { + "oneOf": [ + { + "type": "string" + }, + {} + ] + }, + "EvmTransactionDataSignature": { + "type": "object", + "required": [ + "r", + "s", + "v", + "sig" + ], + "properties": { + "r": { + "type": "string" + }, + "s": { + "type": "string" + }, + "sig": { + "type": "string" + }, + "v": { + "type": "integer", + "format": "int32", + "minimum": 0 + } + } + }, + "EvmTransactionRequest": { + "type": "object", + "required": [ + "value" + ], + "properties": { + "data": { + "type": "string" + }, + "gas_limit": { + "type": [ + "integer", + "null" + ], + "format": "int64", + "minimum": 0 + }, + "gas_price": { + "type": "integer", + "minimum": 0 + }, + "max_fee_per_gas": { + "type": "integer", + "minimum": 0 + }, + "max_priority_fee_per_gas": { + "type": "integer", + "minimum": 0 + }, + "speed": { + "$ref": "#/components/schemas/Speed" + }, + "to": { + "type": "string" + }, + "valid_until": { + "type": "string" + }, + "value": { + "type": "integer", + "format": "u128", + "minimum": 0 + } + } + }, + "EvmTransactionResponse": { + "type": "object", + "required": [ + "id", + "status", + "created_at", + "value", + "from", + "relayer_id" + ], + "properties": { + "confirmed_at": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "data": { + "type": "string" + }, + "from": { + "type": "string" + }, + "gas_limit": { + "type": [ + "integer", + "null" + ], + "format": "int64", + "minimum": 0 + }, + "gas_price": { + "type": "integer", + "minimum": 0 + }, + "hash": { + "type": "string" + }, + "id": { + "type": "string" + }, + "max_fee_per_gas": { + "type": "integer", + "minimum": 0 + }, + "max_priority_fee_per_gas": { + "type": "integer", + "minimum": 0 + }, + "nonce": { + "type": "integer", + "format": "int64", + "minimum": 0 + }, + "relayer_id": { + "type": "string" + }, + "sent_at": { + "type": "string" + }, + "signature": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/EvmTransactionDataSignature" + } + ] + }, + "speed": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/Speed" + } + ] + }, + "status": { + "$ref": "#/components/schemas/TransactionStatus" + }, + "status_reason": { + "type": [ + "string", + "null" + ] + }, + "to": { + "type": "string" + }, + "value": { + "type": "string" + } + } + }, + "FeeEstimateRequestParams": { + "type": "object", + "required": [ + "transaction", + "fee_token" + ], + "properties": { + "fee_token": { + "type": "string" + }, + "transaction": { + "$ref": "#/components/schemas/EncodedSerializedTransaction" + } + }, + "additionalProperties": false + }, + "FeeEstimateResult": { + "type": "object", + "required": [ + "estimated_fee", + "conversion_rate" + ], + "properties": { + "conversion_rate": { + "type": "string" + }, + "estimated_fee": { + "type": "string" + } + } + }, + "GetFeaturesEnabledRequestParams": { + "type": "object", + "additionalProperties": false + }, + "GetFeaturesEnabledResult": { + "type": "object", + "required": [ + "features" + ], + "properties": { + "features": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "GetSupportedTokensItem": { + "type": "object", + "required": [ + "mint", + "symbol", + "decimals" + ], + "properties": { + "conversion_slippage_percentage": { + "type": "number", + "format": "float" + }, + "decimals": { + "type": "integer", + "format": "int32", + "minimum": 0 + }, + "max_allowed_fee": { + "type": "integer", + "format": "int64", + "minimum": 0 + }, + "mint": { + "type": "string" + }, + "symbol": { + "type": "string" + } + } + }, + "GetSupportedTokensRequestParams": { + "type": "object", + "additionalProperties": false + }, + "GetSupportedTokensResult": { + "type": "object", + "required": [ + "tokens" + ], + "properties": { + "tokens": { + "type": "array", + "items": { + "$ref": "#/components/schemas/GetSupportedTokensItem" + } + } + } + }, + "GoogleCloudKmsSignerKeyRequestConfig": { + "type": "object", + "description": "Google Cloud KMS key configuration for API requests", + "required": [ + "location", + "key_ring_id", + "key_id", + "key_version" + ], + "properties": { + "key_id": { + "type": "string" + }, + "key_ring_id": { + "type": "string" + }, + "key_version": { + "type": "integer", + "format": "int32", + "minimum": 0 + }, + "location": { + "type": "string" + } + }, + "additionalProperties": false + }, + "GoogleCloudKmsSignerKeyResponseConfig": { + "type": "object", + "required": [ + "location", + "key_ring_id", + "key_id", + "key_version" + ], + "properties": { + "key_id": { + "type": "string" + }, + "key_ring_id": { + "type": "string" + }, + "key_version": { + "type": "integer", + "format": "int32", + "minimum": 0 + }, + "location": { + "type": "string" + } + } + }, + "GoogleCloudKmsSignerRequestConfig": { + "type": "object", + "description": "Google Cloud KMS signer configuration for API requests", + "required": [ + "service_account", + "key" + ], + "properties": { + "key": { + "$ref": "#/components/schemas/GoogleCloudKmsSignerKeyRequestConfig" + }, + "service_account": { + "$ref": "#/components/schemas/GoogleCloudKmsSignerServiceAccountRequestConfig" + } + }, + "additionalProperties": false + }, + "GoogleCloudKmsSignerServiceAccountRequestConfig": { + "type": "object", + "description": "Google Cloud KMS service account configuration for API requests", + "required": [ + "private_key", + "private_key_id", + "project_id", + "client_email", + "client_id", + "auth_uri", + "token_uri", + "auth_provider_x509_cert_url", + "client_x509_cert_url", + "universe_domain" + ], + "properties": { + "auth_provider_x509_cert_url": { + "type": "string" + }, + "auth_uri": { + "type": "string" + }, + "client_email": { + "type": "string" + }, + "client_id": { + "type": "string" + }, + "client_x509_cert_url": { + "type": "string" + }, + "private_key": { + "type": "string" + }, + "private_key_id": { + "type": "string" + }, + "project_id": { + "type": "string" + }, + "token_uri": { + "type": "string" + }, + "universe_domain": { + "type": "string" + } + }, + "additionalProperties": false + }, + "GoogleCloudKmsSignerServiceAccountResponseConfig": { + "type": "object", + "required": [ + "project_id", + "client_id", + "auth_uri", + "token_uri", + "auth_provider_x509_cert_url", + "client_x509_cert_url", + "universe_domain" + ], + "properties": { + "auth_provider_x509_cert_url": { + "type": "string" + }, + "auth_uri": { + "type": "string" + }, + "client_id": { + "type": "string" + }, + "client_x509_cert_url": { + "type": "string" + }, + "project_id": { + "type": "string" + }, + "token_uri": { + "type": "string" + }, + "universe_domain": { + "type": "string" + } + } + }, + "JsonRpcError": { + "type": "object", + "description": "JSON-RPC 2.0 Error structure.\n\nRepresents an error in a JSON-RPC response.", + "required": [ + "code", + "message", + "description" + ], + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "description": { + "type": "string" + }, + "message": { + "type": "string" + } + } + }, + "JsonRpcId": { + "oneOf": [ + { + "type": "string", + "description": "String identifier" + }, + { + "type": "integer", + "format": "int64", + "description": "Numeric identifier (should not contain fractional parts per spec)" + } + ], + "description": "Represents a JSON-RPC 2.0 ID value.\nAccording to the spec, the ID can be a String or Number.\nWhen used in `Option`: Some(id) = actual ID, None = explicit null." + }, + "JsonRpcRequest_NetworkRpcRequest": { + "allOf": [ + { + "oneOf": [ + { + "$ref": "#/components/schemas/SolanaRpcRequest" + }, + { + "$ref": "#/components/schemas/StellarRpcRequest" + }, + { + "$ref": "#/components/schemas/EvmRpcRequest" + } + ] + }, + { + "type": "object", + "required": [ + "jsonrpc" + ], + "properties": { + "id": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/JsonRpcId" + } + ] + }, + "jsonrpc": { + "type": "string" + } + } + } + ], + "description": "JSON-RPC 2.0 Request structure.\n\nRepresents a JSON-RPC request with proper ID handling:\n- `Some(JsonRpcId)` = request with ID\n- `None` = explicit null ID or notification" + }, + "JsonRpcResponse_NetworkRpcResult": { + "type": "object", + "description": "JSON-RPC 2.0 Response structure.\n\nRepresents a JSON-RPC response that can contain either a result or an error.", + "required": [ + "jsonrpc" + ], + "properties": { + "error": { + "$ref": "#/components/schemas/JsonRpcError" + }, + "id": { + "$ref": "#/components/schemas/JsonRpcId" + }, + "jsonrpc": { + "type": "string" + }, + "result": { + "oneOf": [ + { + "$ref": "#/components/schemas/SolanaRpcResult" + }, + { + "$ref": "#/components/schemas/StellarRpcResult" + }, + { + "$ref": "#/components/schemas/EvmRpcResult" + } + ] + } + } + }, + "JupiterSwapOptions": { + "type": "object", + "description": "Jupiter swap options", + "properties": { + "dynamic_compute_unit_limit": { + "type": "boolean" + }, + "priority_fee_max_lamports": { + "type": "integer", + "format": "int64", + "description": "Maximum priority fee (in lamports) for a transaction. Optional.", + "minimum": 0 + }, + "priority_level": { + "type": "string", + "description": "Priority. Optional." + } + }, + "additionalProperties": false + }, + "LocalSignerRequestConfig": { + "type": "object", + "description": "Local signer configuration for API requests", + "required": [ + "key" + ], + "properties": { + "key": { + "type": "string" + } + }, + "additionalProperties": false + }, + "LogEntry": { + "type": "object", + "required": [ + "level", + "message" + ], + "properties": { + "level": { + "$ref": "#/components/schemas/LogLevel" + }, + "message": { + "type": "string" + } + } + }, + "LogLevel": { + "type": "string", + "enum": [ + "log", + "info", + "error", + "warn", + "debug", + "result" + ] + }, + "MemoSpec": { + "oneOf": [ + { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ] + } + } + }, + { + "type": "object", + "required": [ + "value", + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "text" + ] + }, + "value": { + "type": "string" + } + } + }, + { + "type": "object", + "required": [ + "value", + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "id" + ] + }, + "value": { + "type": "integer", + "format": "int64", + "minimum": 0 + } + } + }, + { + "type": "object", + "required": [ + "value", + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ] + }, + "value": { + "type": "array", + "items": { + "type": "integer", + "format": "int32", + "minimum": 0 + } + } + } + }, + { + "type": "object", + "required": [ + "value", + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "return" + ] + }, + "value": { + "type": "array", + "items": { + "type": "integer", + "format": "int32", + "minimum": 0 + } + } + } + } + ] + }, + "NetworkPolicyResponse": { + "allOf": [ + { + "$ref": "#/components/schemas/RelayerNetworkPolicy" + } + ], + "description": "Network policy response models for OpenAPI documentation" + }, + "NetworkRpcRequest": { + "oneOf": [ + { + "$ref": "#/components/schemas/SolanaRpcRequest" + }, + { + "$ref": "#/components/schemas/StellarRpcRequest" + }, + { + "$ref": "#/components/schemas/EvmRpcRequest" + } + ] + }, + "NetworkRpcResult": { + "oneOf": [ + { + "$ref": "#/components/schemas/SolanaRpcResult" + }, + { + "$ref": "#/components/schemas/StellarRpcResult" + }, + { + "$ref": "#/components/schemas/EvmRpcResult" + } + ] + }, + "NetworkTransactionRequest": { + "oneOf": [ + { + "$ref": "#/components/schemas/EvmTransactionRequest" + }, + { + "$ref": "#/components/schemas/SolanaTransactionRequest" + }, + { + "$ref": "#/components/schemas/StellarTransactionRequest" + } + ] + }, + "NotificationCreateRequest": { + "type": "object", + "description": "Request structure for creating a new notification", + "required": [ + "url" + ], + "properties": { + "id": { + "type": "string" + }, + "signing_key": { + "type": "string", + "description": "Optional signing key for securing webhook notifications" + }, + "type": { + "$ref": "#/components/schemas/NotificationType" + }, + "url": { + "type": "string" + } + }, + "additionalProperties": false + }, + "NotificationResponse": { + "type": "object", + "description": "Response structure for notification API endpoints", + "required": [ + "id", + "type", + "url", + "has_signing_key" + ], + "properties": { + "has_signing_key": { + "type": "boolean", + "description": "Signing key is hidden in responses for security" + }, + "id": { + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/NotificationType" + }, + "url": { + "type": "string" + } + } + }, + "NotificationType": { + "type": "string", + "description": "Notification type enum used by both config file and API", + "enum": [ + "webhook" + ] + }, + "NotificationUpdateRequest": { + "type": "object", + "description": "Request structure for updating an existing notification", + "properties": { + "signing_key": { + "type": [ + "string", + "null" + ], + "description": "Optional signing key for securing webhook notifications.\n- None: don't change the existing signing key\n- Some(\"\"): remove the signing key\n- Some(\"key\"): set the signing key to the provided value" + }, + "type": { + "$ref": "#/components/schemas/NotificationType" + }, + "url": { + "type": "string" + } + }, + "additionalProperties": false + }, + "OperationSpec": { + "oneOf": [ + { + "type": "object", + "required": [ + "destination", + "amount", + "asset", + "type" + ], + "properties": { + "amount": { + "type": "integer", + "format": "int64" + }, + "asset": { + "$ref": "#/components/schemas/AssetSpec" + }, + "destination": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "payment" + ] + } + } + }, + { + "type": "object", + "required": [ + "contract_address", + "function_name", + "args", + "type" + ], + "properties": { + "args": { + "type": "array", + "items": {} + }, + "auth": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/AuthSpec" + } + ] + }, + "contract_address": { + "type": "string" + }, + "function_name": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "invoke_contract" + ] + } + } + }, + { + "type": "object", + "required": [ + "source", + "wasm_hash", + "type" + ], + "properties": { + "auth": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/AuthSpec" + } + ] + }, + "constructor_args": { + "type": [ + "array", + "null" + ], + "items": {} + }, + "salt": { + "type": [ + "string", + "null" + ] + }, + "source": { + "$ref": "#/components/schemas/ContractSource" + }, + "type": { + "type": "string", + "enum": [ + "create_contract" + ] + }, + "wasm_hash": { + "type": "string" + } + } + }, + { + "type": "object", + "required": [ + "wasm", + "type" + ], + "properties": { + "auth": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/AuthSpec" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "upload_wasm" + ] + }, + "wasm": { + "$ref": "#/components/schemas/WasmSource" + } + } + } + ] + }, + "PaginationMeta": { + "type": "object", + "required": [ + "current_page", + "per_page", + "total_items" + ], + "properties": { + "current_page": { + "type": "integer", + "format": "int32", + "minimum": 0 + }, + "per_page": { + "type": "integer", + "format": "int32", + "minimum": 0 + }, + "total_items": { + "type": "integer", + "format": "int64", + "minimum": 0 + } + } + }, + "PluginCallRequest": { + "type": "object", + "required": [ + "params" + ], + "properties": { + "params": { + "description": "Plugin parameters" + } + } + }, + "PluginCallResponse": { + "type": "object", + "required": [ + "success", + "return_value", + "message", + "logs", + "error", + "traces" + ], + "properties": { + "error": { + "type": "string" + }, + "logs": { + "type": "array", + "items": { + "$ref": "#/components/schemas/LogEntry" + } + }, + "message": { + "type": "string" + }, + "return_value": { + "type": "string" + }, + "success": { + "type": "boolean" + }, + "traces": { + "type": "array", + "items": {} + } + } + }, + "PrepareTransactionRequestParams": { + "type": "object", + "required": [ + "transaction", + "fee_token" + ], + "properties": { + "fee_token": { + "type": "string" + }, + "transaction": { + "$ref": "#/components/schemas/EncodedSerializedTransaction" + } + }, + "additionalProperties": false + }, + "PrepareTransactionResult": { + "type": "object", + "required": [ + "transaction", + "fee_in_spl", + "fee_in_lamports", + "fee_token", + "valid_until_blockheight" + ], + "properties": { + "fee_in_lamports": { + "type": "string" + }, + "fee_in_spl": { + "type": "string" + }, + "fee_token": { + "type": "string" + }, + "transaction": { + "$ref": "#/components/schemas/EncodedSerializedTransaction" + }, + "valid_until_blockheight": { + "type": "integer", + "format": "int64", + "minimum": 0 + } + } + }, + "RelayerEvmPolicy": { + "type": "object", + "description": "EVM-specific relayer policy configuration", + "properties": { + "eip1559_pricing": { + "type": [ + "boolean", + "null" + ] + }, + "gas_limit_estimation": { + "type": [ + "boolean", + "null" + ] + }, + "gas_price_cap": { + "type": [ + "integer", + "null" + ], + "minimum": 0 + }, + "min_balance": { + "type": [ + "integer", + "null" + ], + "minimum": 0 + }, + "private_transactions": { + "type": [ + "boolean", + "null" + ] + }, + "whitelist_receivers": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + } + }, + "additionalProperties": false + }, + "RelayerNetworkPolicy": { + "oneOf": [ + { + "allOf": [ + { + "$ref": "#/components/schemas/RelayerEvmPolicy" + }, + { + "type": "object", + "required": [ + "network_type" + ], + "properties": { + "network_type": { + "type": "string", + "enum": [ + "evm" + ] + } + } + } + ] + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/RelayerSolanaPolicy" + }, + { + "type": "object", + "required": [ + "network_type" + ], + "properties": { + "network_type": { + "type": "string", + "enum": [ + "solana" + ] + } + } + } + ] + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/RelayerStellarPolicy" + }, + { + "type": "object", + "required": [ + "network_type" + ], + "properties": { + "network_type": { + "type": "string", + "enum": [ + "stellar" + ] + } + } + } + ] + } + ], + "description": "Network-specific policy for relayers" + }, + "RelayerNetworkPolicyResponse": { + "oneOf": [ + { + "$ref": "#/components/schemas/EvmPolicyResponse" + }, + { + "$ref": "#/components/schemas/StellarPolicyResponse" + }, + { + "$ref": "#/components/schemas/SolanaPolicyResponse" + } + ], + "description": "Policy types for responses - these don't include network_type tags\nsince the network_type is already available at the top level of RelayerResponse" + }, + "RelayerNetworkType": { + "type": "string", + "description": "Network type enum for relayers", + "enum": [ + "evm", + "solana", + "stellar" + ] + }, + "RelayerResponse": { + "type": "object", + "description": "Relayer response model for API endpoints", + "required": [ + "id", + "name", + "network", + "network_type", + "paused", + "signer_id" + ], + "properties": { + "address": { + "type": "string" + }, + "custom_rpc_urls": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RpcConfig" + } + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "network": { + "type": "string" + }, + "network_type": { + "$ref": "#/components/schemas/RelayerNetworkType" + }, + "notification_id": { + "type": "string" + }, + "paused": { + "type": "boolean" + }, + "policies": { + "$ref": "#/components/schemas/RelayerNetworkPolicyResponse", + "description": "Policies without redundant network_type tag - network type is available at top level\nOnly included if user explicitly provided policies (not shown for empty/default policies)" + }, + "signer_id": { + "type": "string" + }, + "system_disabled": { + "type": "boolean" + } + } + }, + "RelayerSolanaPolicy": { + "type": "object", + "description": "Solana-specific relayer policy configuration", + "properties": { + "allowed_accounts": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "allowed_programs": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "allowed_tokens": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/components/schemas/SolanaAllowedTokensPolicy" + } + }, + "disallowed_accounts": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "fee_margin_percentage": { + "type": [ + "number", + "null" + ], + "format": "float" + }, + "fee_payment_strategy": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/SolanaFeePaymentStrategy" + } + ] + }, + "max_allowed_fee_lamports": { + "type": [ + "integer", + "null" + ], + "format": "int64", + "minimum": 0 + }, + "max_signatures": { + "type": [ + "integer", + "null" + ], + "format": "int32", + "minimum": 0 + }, + "max_tx_data_size": { + "type": [ + "integer", + "null" + ], + "format": "int32", + "minimum": 0 + }, + "min_balance": { + "type": [ + "integer", + "null" + ], + "format": "int64", + "minimum": 0 + }, + "swap_config": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/RelayerSolanaSwapConfig" + } + ] + } + }, + "additionalProperties": false + }, + "RelayerSolanaSwapConfig": { + "type": "object", + "description": "Solana swap policy configuration", + "properties": { + "cron_schedule": { + "type": "string", + "description": "Cron schedule for executing token swap logic to keep relayer funded. Optional." + }, + "jupiter_swap_options": { + "$ref": "#/components/schemas/JupiterSwapOptions", + "description": "Swap options for JupiterSwap strategy. Optional." + }, + "min_balance_threshold": { + "type": "integer", + "format": "int64", + "description": "Min sol balance to execute token swap logic to keep relayer funded. Optional.", + "minimum": 0 + }, + "strategy": { + "$ref": "#/components/schemas/SolanaSwapStrategy", + "description": "DEX strategy to use for token swaps." + } + }, + "additionalProperties": false + }, + "RelayerStatus": { + "oneOf": [ + { + "type": "object", + "required": [ + "balance", + "pending_transactions_count", + "system_disabled", + "paused", + "nonce", + "network_type" + ], + "properties": { + "balance": { + "type": "string" + }, + "last_confirmed_transaction_timestamp": { + "type": [ + "string", + "null" + ] + }, + "network_type": { + "type": "string", + "enum": [ + "evm" + ] + }, + "nonce": { + "type": "string" + }, + "paused": { + "type": "boolean" + }, + "pending_transactions_count": { + "type": "integer", + "format": "int64", + "minimum": 0 + }, + "system_disabled": { + "type": "boolean" + } + } + }, + { + "type": "object", + "required": [ + "balance", + "pending_transactions_count", + "system_disabled", + "paused", + "sequence_number", + "network_type" + ], + "properties": { + "balance": { + "type": "string" + }, + "last_confirmed_transaction_timestamp": { + "type": [ + "string", + "null" + ] + }, + "network_type": { + "type": "string", + "enum": [ + "stellar" + ] + }, + "paused": { + "type": "boolean" + }, + "pending_transactions_count": { + "type": "integer", + "format": "int64", + "minimum": 0 + }, + "sequence_number": { + "type": "string" + }, + "system_disabled": { + "type": "boolean" + } + } + }, + { + "type": "object", + "required": [ + "balance", + "pending_transactions_count", + "system_disabled", + "paused", + "network_type" + ], + "properties": { + "balance": { + "type": "string" + }, + "last_confirmed_transaction_timestamp": { + "type": [ + "string", + "null" + ] + }, + "network_type": { + "type": "string", + "enum": [ + "solana" + ] + }, + "paused": { + "type": "boolean" + }, + "pending_transactions_count": { + "type": "integer", + "format": "int64", + "minimum": 0 + }, + "system_disabled": { + "type": "boolean" + } + } + } + ], + "description": "Relayer status with runtime information" + }, + "RelayerStellarPolicy": { + "type": "object", + "description": "Stellar-specific relayer policy configuration", + "properties": { + "concurrent_transactions": { + "type": [ + "boolean", + "null" + ] + }, + "max_fee": { + "type": [ + "integer", + "null" + ], + "format": "int32", + "minimum": 0 + }, + "min_balance": { + "type": [ + "integer", + "null" + ], + "format": "int64", + "minimum": 0 + }, + "timeout_seconds": { + "type": [ + "integer", + "null" + ], + "format": "int64", + "minimum": 0 + } + }, + "additionalProperties": false + }, + "RpcConfig": { + "type": "object", + "description": "Configuration for an RPC endpoint.", + "required": [ + "url" + ], + "properties": { + "url": { + "type": "string", + "description": "The RPC endpoint URL." + }, + "weight": { + "type": "integer", + "format": "int32", + "description": "The weight of this endpoint in the weighted round-robin selection.\nDefaults to DEFAULT_RPC_WEIGHT (100). Should be between 0 and 100.", + "minimum": 0 + } + } + }, + "SignAndSendTransactionRequestParams": { + "type": "object", + "required": [ + "transaction" + ], + "properties": { + "transaction": { + "$ref": "#/components/schemas/EncodedSerializedTransaction" + } + }, + "additionalProperties": false + }, + "SignAndSendTransactionResult": { + "type": "object", + "required": [ + "transaction", + "signature", + "id" + ], + "properties": { + "id": { + "type": "string" + }, + "signature": { + "type": "string" + }, + "transaction": { + "$ref": "#/components/schemas/EncodedSerializedTransaction" + } + } + }, + "SignDataRequest": { + "type": "object", + "required": [ + "message" + ], + "properties": { + "message": { + "type": "string" + } + } + }, + "SignDataResponse": { + "oneOf": [ + { + "$ref": "#/components/schemas/SignDataResponseEvm" + }, + { + "$ref": "#/components/schemas/SignDataResponseSolana" + } + ] + }, + "SignDataResponseEvm": { + "type": "object", + "required": [ + "r", + "s", + "v", + "sig" + ], + "properties": { + "r": { + "type": "string" + }, + "s": { + "type": "string" + }, + "sig": { + "type": "string" + }, + "v": { + "type": "integer", + "format": "int32", + "minimum": 0 + } + } + }, + "SignDataResponseSolana": { + "type": "object", + "required": [ + "signature", + "public_key" + ], + "properties": { + "public_key": { + "type": "string" + }, + "signature": { + "type": "string" + } + } + }, + "SignTransactionRequest": { + "oneOf": [ + { + "$ref": "#/components/schemas/SignTransactionRequestStellar" + }, + { + "type": "array", + "items": { + "type": "integer", + "format": "int32", + "minimum": 0 + } + }, + { + "type": "array", + "items": { + "type": "integer", + "format": "int32", + "minimum": 0 + } + } + ] + }, + "SignTransactionRequestParams": { + "type": "object", + "required": [ + "transaction" + ], + "properties": { + "transaction": { + "$ref": "#/components/schemas/EncodedSerializedTransaction" + } + }, + "additionalProperties": false + }, + "SignTransactionRequestStellar": { + "type": "object", + "required": [ + "unsigned_xdr" + ], + "properties": { + "unsigned_xdr": { + "type": "string" + } + } + }, + "SignTransactionResponse": { + "oneOf": [ + { + "$ref": "#/components/schemas/SignTransactionResponseStellar" + }, + { + "type": "array", + "items": { + "type": "integer", + "format": "int32", + "minimum": 0 + } + }, + { + "type": "array", + "items": { + "type": "integer", + "format": "int32", + "minimum": 0 + } + } + ] + }, + "SignTransactionResponseStellar": { + "type": "object", + "required": [ + "signedXdr", + "signature" + ], + "properties": { + "signature": { + "type": "string" + }, + "signedXdr": { + "type": "string" + } + } + }, + "SignTransactionResult": { + "type": "object", + "required": [ + "transaction", + "signature" + ], + "properties": { + "signature": { + "type": "string" + }, + "transaction": { + "$ref": "#/components/schemas/EncodedSerializedTransaction" + } + } + }, + "SignTypedDataRequest": { + "type": "object", + "required": [ + "domain_separator", + "hash_struct_message" + ], + "properties": { + "domain_separator": { + "type": "string" + }, + "hash_struct_message": { + "type": "string" + } + } + }, + "SignerConfigRequest": { + "oneOf": [ + { + "$ref": "#/components/schemas/LocalSignerRequestConfig" + }, + { + "$ref": "#/components/schemas/AwsKmsSignerRequestConfig" + }, + { + "$ref": "#/components/schemas/VaultSignerRequestConfig" + }, + { + "$ref": "#/components/schemas/VaultTransitSignerRequestConfig" + }, + { + "$ref": "#/components/schemas/TurnkeySignerRequestConfig" + }, + { + "$ref": "#/components/schemas/GoogleCloudKmsSignerRequestConfig" + } + ], + "description": "Signer configuration enum for API requests (without type discriminator)" + }, + "SignerConfigResponse": { + "oneOf": [ + { + "type": "object", + "required": [ + "address", + "key_name" + ], + "properties": { + "address": { + "type": "string" + }, + "key_name": { + "type": "string" + }, + "mount_point": { + "type": [ + "string", + "null" + ] + }, + "namespace": { + "type": [ + "string", + "null" + ] + } + } + }, + { + "type": "object", + "required": [ + "key_name", + "address", + "pubkey" + ], + "properties": { + "address": { + "type": "string" + }, + "key_name": { + "type": "string" + }, + "mount_point": { + "type": [ + "string", + "null" + ] + }, + "namespace": { + "type": [ + "string", + "null" + ] + }, + "pubkey": { + "type": "string" + } + } + }, + { + "type": "object", + "required": [ + "key_id" + ], + "properties": { + "key_id": { + "type": "string" + }, + "region": { + "type": [ + "string", + "null" + ] + } + } + }, + { + "type": "object", + "required": [ + "api_public_key", + "organization_id", + "private_key_id", + "public_key" + ], + "properties": { + "api_public_key": { + "type": "string" + }, + "organization_id": { + "type": "string" + }, + "private_key_id": { + "type": "string" + }, + "public_key": { + "type": "string" + } + } + }, + { + "type": "object", + "required": [ + "service_account", + "key" + ], + "properties": { + "key": { + "$ref": "#/components/schemas/GoogleCloudKmsSignerKeyResponseConfig" + }, + "service_account": { + "$ref": "#/components/schemas/GoogleCloudKmsSignerServiceAccountResponseConfig" + } + } + }, + { + "type": "object" + } + ], + "description": "Signer configuration response\nDoes not include sensitive information like private keys" + }, + "SignerCreateRequest": { + "type": "object", + "description": "Request model for creating a new signer", + "required": [ + "type", + "config" + ], + "properties": { + "config": { + "$ref": "#/components/schemas/SignerConfigRequest", + "description": "The signer configuration" + }, + "id": { + "type": "string", + "description": "Optional ID - if not provided, a UUID will be generated" + }, + "type": { + "$ref": "#/components/schemas/SignerTypeRequest", + "description": "The type of signer" + } + }, + "additionalProperties": false + }, + "SignerResponse": { + "type": "object", + "required": [ + "id", + "type", + "config" + ], + "properties": { + "config": { + "$ref": "#/components/schemas/SignerConfigResponse", + "description": "Non-secret configuration details" + }, + "id": { + "type": "string", + "description": "The unique identifier of the signer" + }, + "type": { + "$ref": "#/components/schemas/SignerType", + "description": "The type of signer (local, aws_kms, google_cloud_kms, vault, etc.)" + } + } + }, + "SignerType": { + "type": "string", + "description": "Signer type enum used for validation and API responses", + "enum": [ + "local", + "aws_kms", + "google_cloud_kms", + "vault", + "vault_transit", + "turnkey" + ] + }, + "SignerTypeRequest": { + "type": "string", + "description": "Signer type enum for API requests", + "enum": [ + "plain", + "aws_kms", + "vault", + "vault_transit", + "turnkey", + "google_cloud_kms" + ] + }, + "SignerUpdateRequest": { + "type": "object", + "description": "Request model for updating an existing signer\nAt the moment, we don't allow updating signers", + "additionalProperties": false + }, + "SolanaAllowedTokensPolicy": { + "type": "object", + "description": "Configuration for allowed token handling on Solana", + "required": [ + "mint" + ], + "properties": { + "decimals": { + "type": "integer", + "format": "int32", + "minimum": 0 + }, + "max_allowed_fee": { + "type": "integer", + "format": "int64", + "minimum": 0 + }, + "mint": { + "type": "string" + }, + "swap_config": { + "$ref": "#/components/schemas/SolanaAllowedTokensSwapConfig" + }, + "symbol": { + "type": "string" + } + }, + "additionalProperties": false + }, + "SolanaAllowedTokensSwapConfig": { + "type": "object", + "description": "Solana token swap configuration", + "properties": { + "max_amount": { + "type": "integer", + "format": "int64", + "description": "Maximum amount of tokens to swap. Optional.", + "minimum": 0 + }, + "min_amount": { + "type": "integer", + "format": "int64", + "description": "Minimum amount of tokens to swap. Optional.", + "minimum": 0 + }, + "retain_min_amount": { + "type": "integer", + "format": "int64", + "description": "Minimum amount of tokens to retain after swap. Optional.", + "minimum": 0 + }, + "slippage_percentage": { + "type": "number", + "format": "float", + "description": "Conversion slippage percentage for token. Optional." + } + }, + "additionalProperties": false + }, + "SolanaFeePaymentStrategy": { + "type": "string", + "description": "Solana fee payment strategy", + "enum": [ + "user", + "relayer" + ] + }, + "SolanaPolicyResponse": { + "type": "object", + "description": "Solana policy response model for OpenAPI documentation", + "properties": { + "allowed_accounts": { + "type": "array", + "items": { + "type": "string" + } + }, + "allowed_programs": { + "type": "array", + "items": { + "type": "string" + } + }, + "allowed_tokens": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SolanaAllowedTokensPolicy" + } + }, + "disallowed_accounts": { + "type": "array", + "items": { + "type": "string" + } + }, + "fee_margin_percentage": { + "type": "number", + "format": "float" + }, + "fee_payment_strategy": { + "$ref": "#/components/schemas/SolanaFeePaymentStrategy" + }, + "max_allowed_fee_lamports": { + "type": "integer", + "format": "int64", + "minimum": 0 + }, + "max_signatures": { + "type": "integer", + "format": "int32", + "minimum": 0 + }, + "max_tx_data_size": { + "type": "integer", + "format": "int32", + "minimum": 0 + }, + "min_balance": { + "type": "integer", + "format": "int64", + "minimum": 0 + }, + "swap_config": { + "$ref": "#/components/schemas/RelayerSolanaSwapConfig" + } + }, + "additionalProperties": false + }, + "SolanaRpcRequest": { + "oneOf": [ + { + "type": "object", + "required": [ + "params", + "method" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "feeEstimate" + ] + }, + "params": { + "$ref": "#/components/schemas/FeeEstimateRequestParams" + } + }, + "example": "feeEstimate" + }, + { + "type": "object", + "required": [ + "params", + "method" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "transferTransaction" + ] + }, + "params": { + "$ref": "#/components/schemas/TransferTransactionRequestParams" + } + }, + "example": "transferTransaction" + }, + { + "type": "object", + "required": [ + "params", + "method" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "prepareTransaction" + ] + }, + "params": { + "$ref": "#/components/schemas/PrepareTransactionRequestParams" + } + }, + "example": "prepareTransaction" + }, + { + "type": "object", + "required": [ + "params", + "method" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "signTransaction" + ] + }, + "params": { + "$ref": "#/components/schemas/SignTransactionRequestParams" + } + }, + "example": "signTransaction" + }, + { + "type": "object", + "required": [ + "params", + "method" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "signAndSendTransaction" + ] + }, + "params": { + "$ref": "#/components/schemas/SignAndSendTransactionRequestParams" + } + }, + "example": "signAndSendTransaction" + }, + { + "type": "object", + "required": [ + "params", + "method" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "getSupportedTokens" + ] + }, + "params": { + "$ref": "#/components/schemas/GetSupportedTokensRequestParams" + } + }, + "example": "getSupportedTokens" + }, + { + "type": "object", + "required": [ + "params", + "method" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "getFeaturesEnabled" + ] + }, + "params": { + "$ref": "#/components/schemas/GetFeaturesEnabledRequestParams" + } + }, + "example": "getFeaturesEnabled" + } + ] + }, + "SolanaRpcResult": { + "oneOf": [ + { + "allOf": [ + { + "$ref": "#/components/schemas/FeeEstimateResult" + }, + { + "type": "object", + "required": [ + "method" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "feeEstimate" + ] + } + } + } + ] + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/TransferTransactionResult" + }, + { + "type": "object", + "required": [ + "method" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "transferTransaction" + ] + } + } + } + ] + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/PrepareTransactionResult" + }, + { + "type": "object", + "required": [ + "method" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "prepareTransaction" + ] + } + } + } + ] + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/SignTransactionResult" + }, + { + "type": "object", + "required": [ + "method" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "signTransaction" + ] + } + } + } + ] + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/SignAndSendTransactionResult" + }, + { + "type": "object", + "required": [ + "method" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "signAndSendTransaction" + ] + } + } + } + ] + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/GetSupportedTokensResult" + }, + { + "type": "object", + "required": [ + "method" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "getSupportedTokens" + ] + } + } + } + ] + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/GetFeaturesEnabledResult" + }, + { + "type": "object", + "required": [ + "method" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "getFeaturesEnabled" + ] + } + } + } + ] + } + ] + }, + "SolanaSwapStrategy": { + "type": "string", + "description": "Solana swap strategy", + "enum": [ + "jupiter-swap", + "jupiter-ultra", + "noop" + ] + }, + "SolanaTransactionRequest": { + "type": "object", + "required": [ + "transaction" + ], + "properties": { + "transaction": { + "$ref": "#/components/schemas/EncodedSerializedTransaction" + } + } + }, + "SolanaTransactionResponse": { + "type": "object", + "required": [ + "id", + "status", + "created_at", + "transaction" + ], + "properties": { + "confirmed_at": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "id": { + "type": "string" + }, + "sent_at": { + "type": "string" + }, + "signature": { + "type": "string" + }, + "status": { + "$ref": "#/components/schemas/TransactionStatus" + }, + "status_reason": { + "type": [ + "string", + "null" + ] + }, + "transaction": { + "type": "string" + } + } + }, + "Speed": { + "type": "string", + "enum": [ + "fastest", + "fast", + "average", + "safeLow" + ] + }, + "StellarPolicyResponse": { + "type": "object", + "description": "Stellar policy response model for OpenAPI documentation", + "properties": { + "concurrent_transactions": { + "type": "boolean" + }, + "max_fee": { + "type": "integer", + "format": "int32", + "minimum": 0 + }, + "min_balance": { + "type": "integer", + "format": "int64", + "minimum": 0 + }, + "timeout_seconds": { + "type": "integer", + "format": "int64", + "minimum": 0 + } + }, + "additionalProperties": false + }, + "StellarRpcRequest": { + "oneOf": [ + { + "type": "object", + "required": [ + "params", + "method" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "GenericRpcRequest" + ] + }, + "params": { + "type": "string" + } + } + } + ] + }, + "StellarRpcResult": { + "oneOf": [ + { + "type": "string" + } + ] + }, + "StellarTransactionRequest": { + "type": "object", + "required": [ + "network" + ], + "properties": { + "fee_bump": { + "type": [ + "boolean", + "null" + ], + "description": "Explicitly request fee-bump wrapper\nOnly valid when transaction_xdr contains a signed transaction" + }, + "max_fee": { + "type": [ + "integer", + "null" + ], + "format": "int64", + "description": "Maximum fee in stroops (defaults to 0.1 XLM = 1,000,000 stroops)" + }, + "memo": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/MemoSpec" + } + ] + }, + "network": { + "type": "string" + }, + "operations": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/components/schemas/OperationSpec" + } + }, + "source_account": { + "type": [ + "string", + "null" + ] + }, + "transaction_xdr": { + "type": [ + "string", + "null" + ], + "description": "Pre-built transaction XDR (base64 encoded, signed or unsigned)\nMutually exclusive with operations field" + }, + "valid_until": { + "type": [ + "string", + "null" + ] + } + } + }, + "StellarTransactionResponse": { + "type": "object", + "required": [ + "id", + "status", + "created_at", + "source_account", + "fee", + "sequence_number" + ], + "properties": { + "confirmed_at": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "fee": { + "type": "integer", + "format": "int32", + "minimum": 0 + }, + "hash": { + "type": "string" + }, + "id": { + "type": "string" + }, + "sent_at": { + "type": "string" + }, + "sequence_number": { + "type": "integer", + "format": "int64" + }, + "source_account": { + "type": "string" + }, + "status": { + "$ref": "#/components/schemas/TransactionStatus" + }, + "status_reason": { + "type": [ + "string", + "null" + ] + } + } + }, + "TransactionResponse": { + "oneOf": [ + { + "$ref": "#/components/schemas/EvmTransactionResponse" + }, + { + "$ref": "#/components/schemas/SolanaTransactionResponse" + }, + { + "$ref": "#/components/schemas/StellarTransactionResponse" + } + ] + }, + "TransactionStatus": { + "type": "string", + "enum": [ + "canceled", + "pending", + "sent", + "submitted", + "mined", + "confirmed", + "failed", + "expired" + ] + }, + "TransferTransactionRequestParams": { + "type": "object", + "required": [ + "amount", + "token", + "source", + "destination" + ], + "properties": { + "amount": { + "type": "integer", + "format": "int64", + "minimum": 0 + }, + "destination": { + "type": "string" + }, + "source": { + "type": "string" + }, + "token": { + "type": "string" + } + }, + "additionalProperties": false + }, + "TransferTransactionResult": { + "type": "object", + "required": [ + "transaction", + "fee_in_spl", + "fee_in_lamports", + "fee_token", + "valid_until_blockheight" + ], + "properties": { + "fee_in_lamports": { + "type": "string" + }, + "fee_in_spl": { + "type": "string" + }, + "fee_token": { + "type": "string" + }, + "transaction": { + "$ref": "#/components/schemas/EncodedSerializedTransaction" + }, + "valid_until_blockheight": { + "type": "integer", + "format": "int64", + "minimum": 0 + } + } + }, + "TurnkeySignerRequestConfig": { + "type": "object", + "description": "Turnkey signer configuration for API requests", + "required": [ + "api_public_key", + "api_private_key", + "organization_id", + "private_key_id", + "public_key" + ], + "properties": { + "api_private_key": { + "type": "string" + }, + "api_public_key": { + "type": "string" + }, + "organization_id": { + "type": "string" + }, + "private_key_id": { + "type": "string" + }, + "public_key": { + "type": "string" + } + }, + "additionalProperties": false + }, + "UpdateRelayerRequest": { + "type": "object", + "properties": { + "custom_rpc_urls": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/components/schemas/RpcConfig" + } + }, + "name": { + "type": [ + "string", + "null" + ] + }, + "notification_id": { + "type": [ + "string", + "null" + ] + }, + "paused": { + "type": "boolean" + }, + "policies": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/CreateRelayerPolicyRequest", + "description": "Raw policy JSON - will be validated against relayer's network type during application" + } + ] + } + }, + "additionalProperties": false + }, + "VaultSignerRequestConfig": { + "type": "object", + "description": "Vault signer configuration for API requests", + "required": [ + "address", + "role_id", + "secret_id", + "key_name" + ], + "properties": { + "address": { + "type": "string" + }, + "key_name": { + "type": "string" + }, + "mount_point": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "role_id": { + "type": "string" + }, + "secret_id": { + "type": "string" + } + }, + "additionalProperties": false + }, + "VaultTransitSignerRequestConfig": { + "type": "object", + "description": "Vault Transit signer configuration for API requests", + "required": [ + "key_name", + "address", + "role_id", + "secret_id", + "pubkey" + ], + "properties": { + "address": { + "type": "string" + }, + "key_name": { + "type": "string" + }, + "mount_point": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "pubkey": { + "type": "string" + }, + "role_id": { + "type": "string" + }, + "secret_id": { + "type": "string" + } + }, + "additionalProperties": false + }, + "WasmSource": { + "oneOf": [ + { + "type": "object", + "required": [ + "hex" + ], + "properties": { + "hex": { + "type": "string" + } + } + }, + { + "type": "object", + "required": [ + "base64" + ], + "properties": { + "base64": { + "type": "string" + } + } + } + ], + "description": "Represents different ways to provide WASM code" + } + }, + "securitySchemes": { + "bearer_auth": { + "type": "http", + "scheme": "bearer" + } + } + }, + "tags": [ + { + "name": "Relayers", + "description": "Relayers are the core components of the OpenZeppelin Relayer API. They are responsible for executing transactions on behalf of users and providing a secure and reliable way to interact with the blockchain." + }, + { + "name": "Plugins", + "description": "Plugins are TypeScript functions that can be used to extend the OpenZeppelin Relayer API functionality." + }, + { + "name": "Notifications", + "description": "Notifications are responsible for showing the notifications related to the relayers." + }, + { + "name": "Signers", + "description": "Signers are responsible for signing the transactions related to the relayers." + }, + { + "name": "Metrics", + "description": "Metrics are responsible for showing the metrics related to the relayers." + }, + { + "name": "Health", + "description": "Health is responsible for showing the health of the relayers." + } + ] +} diff --git a/src/lib/openapi.ts b/src/lib/openapi.ts index 8bfcb7ae..240767c5 100644 --- a/src/lib/openapi.ts +++ b/src/lib/openapi.ts @@ -1,8 +1,5 @@ import { createOpenAPI } from "fumadocs-openapi/server"; export const openapi = createOpenAPI({ - // the OpenAPI schema, you can also give it an external URL. - input: [ - "https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-relayer/refs/heads/main/docs/openapi.json", - ], + input: ["./public/schemas/openzeppelin-relayer-openapi.json"], }); From f9dfaeef9770fae92431a8b301d0a430d939ad93 Mon Sep 17 00:00:00 2001 From: Steve Date: Tue, 9 Sep 2025 14:25:06 -0400 Subject: [PATCH 13/14] chore: updated search --- src/app/api/search/route.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/app/api/search/route.ts b/src/app/api/search/route.ts index d4ab9962..313e2bca 100644 --- a/src/app/api/search/route.ts +++ b/src/app/api/search/route.ts @@ -1,7 +1,14 @@ -import { createFromSource } from "fumadocs-core/search/server"; import { source } from "@/lib/source"; +import { createSearchAPI } from "fumadocs-core/search/server"; +import { structure } from "fumadocs-core/mdx-plugins"; -export const { GET } = createFromSource(source, { - // https://docs.orama.com/open-source/supported-languages +export const { GET } = createSearchAPI("advanced", { language: "english", + indexes: source.getPages().map((page) => ({ + title: page.data.title, + description: page.data.description, + url: page.url, + id: page.url, + structuredData: structure(page.data.content), + })), }); From 6d65b03eaf7edb2bbd92416a7c333994a4e7a179 Mon Sep 17 00:00:00 2001 From: Steve Date: Tue, 9 Sep 2025 14:25:52 -0400 Subject: [PATCH 14/14] chore: linting --- src/app/api/search/route.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/api/search/route.ts b/src/app/api/search/route.ts index 313e2bca..b606c96f 100644 --- a/src/app/api/search/route.ts +++ b/src/app/api/search/route.ts @@ -1,6 +1,6 @@ -import { source } from "@/lib/source"; -import { createSearchAPI } from "fumadocs-core/search/server"; import { structure } from "fumadocs-core/mdx-plugins"; +import { createSearchAPI } from "fumadocs-core/search/server"; +import { source } from "@/lib/source"; export const { GET } = createSearchAPI("advanced", { language: "english",