diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index 66e9852e..5d6f28ba 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -3,19 +3,16 @@ name: Build Docs
on:
push:
branches: [master]
-
-permissions:
- contents: write
+ workflow_dispatch: {}
jobs:
build:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
- with:
- submodules: recursive
- - name: Set up environment
- uses: ./.github/actions/setup
- - run: bash scripts/git-user-config.sh
- - run: node scripts/update-docs-branch.js
- - run: git push --all origin
+ - run: |
+ gh workflow run generate-api-docs-community-contracts.yml \
+ --repo OpenZeppelin/docs \
+ --field tag_name="${{ github.sha }}" \
+ --field trigger_type="commit"
+ env:
+ GH_TOKEN: ${{ secrets.DOCS_REPO_TOKEN }}
diff --git a/contracts/access/README.adoc b/contracts/access/README.adoc
deleted file mode 100644
index b42feb43..00000000
--- a/contracts/access/README.adoc
+++ /dev/null
@@ -1,12 +0,0 @@
-= Access
-
-[.readme-notice]
-NOTE: This document is better viewed at https://docs.openzeppelin.com/community-contracts/api/access
-
-This directory contains utility contracts to restrict access control in smart contracts. These include:
-
- * {AccessManagerLight}: A simpler version of an AccessManager that uses `bytes8` roles to allow function calls identified by their 4-bytes selector.
-
-== AccessManager
-
-{{AccessManagerLight}}
diff --git a/contracts/access/README.mdx b/contracts/access/README.mdx
new file mode 100644
index 00000000..d8e1ef3c
--- /dev/null
+++ b/contracts/access/README.mdx
@@ -0,0 +1,15 @@
+---
+title: Access
+---
+
+
+This document is better viewed at https://docs.openzeppelin.com/community-contracts/api/access
+
+
+This directory contains utility contracts to restrict access control in smart contracts. These include:
+
+* AccessManagerLight: A simpler version of an AccessManager that uses `bytes8` roles to allow function calls identified by their 4-bytes selector.
+
+## AccessManager
+
+AccessManagerLight
diff --git a/contracts/account/README.adoc b/contracts/account/README.adoc
deleted file mode 100644
index 56612e13..00000000
--- a/contracts/account/README.adoc
+++ /dev/null
@@ -1,56 +0,0 @@
-= Account
-[.readme-notice]
-NOTE: This document is better viewed at https://docs.openzeppelin.com/community-contracts/api/account
-
-This directory includes contracts to build accounts for ERC-4337. These include:
-
- * {ERC7579Executor}: An executor module that enables executing calls from accounts where the it's installed.
- * {ERC7579DelayedExecutor}: An executor module that adds a delay before executing an account operation.
- * {ERC7579SelectorExecutor}: An executor module that restricts execution to specific function selectors.
- * {ERC7579Validator}: Abstract validator module for ERC-7579 accounts that provides base implementation for signature validation.
- * {ERC7579Signature}: Implementation of {ERC7579Validator} using ERC-7913 signature verification for address-less cryptographic keys and account signatures.
- * {ERC7579Multisig}: An extension of {ERC7579Validator} that enables validation using ERC-7913 signer keys.
- * {ERC7579MultisigWeighted}: An extension of {ERC7579Multisig} that allows different weights to be assigned to signers.
- * {ERC7579MultisigConfirmation}: An extension of {ERC7579Multisig} that requires each signer to provide a confirmation signature.
- * {ERC7579MultisigStorage}: An extension of {ERC7579Multisig} that allows storing presigned approvals in storage.
- * {PaymasterCore}: An ERC-4337 paymaster implementation that includes the core logic to validate and pay for user operations.
- * {PaymasterERC20}: A paymaster that allows users to pay for user operations using ERC-20 tokens.
- * {PaymasterERC20Guarantor}: A paymaster that enables third parties to guarantee user operations by pre-funding gas costs, with the option for users to repay or for guarantors to absorb the cost.
- * {PaymasterERC721Owner}: A paymaster that allows users to pay for user operations based on ERC-721 ownership.
- * {PaymasterSigner}: A paymaster that allows users to pay for user operations using an authorized signature.
-
-== Modules
-
-=== Executors
-
-{{ERC7579Executor}}
-
-{{ERC7579DelayedExecutor}}
-
-{{ERC7579SelectorExecutor}}
-
-=== Validators
-
-{{ERC7579Validator}}
-
-{{ERC7579Signature}}
-
-{{ERC7579Multisig}}
-
-{{ERC7579MultisigWeighted}}
-
-{{ERC7579MultisigConfirmation}}
-
-{{ERC7579MultisigStorage}}
-
-== Paymaster
-
-{{PaymasterCore}}
-
-{{PaymasterERC20}}
-
-{{PaymasterERC20Guarantor}}
-
-{{PaymasterERC721Owner}}
-
-{{PaymasterSigner}}
diff --git a/contracts/account/README.mdx b/contracts/account/README.mdx
new file mode 100644
index 00000000..2ae1a942
--- /dev/null
+++ b/contracts/account/README.mdx
@@ -0,0 +1,60 @@
+---
+title: Account
+---
+
+
+This document is better viewed at https://docs.openzeppelin.com/community-contracts/api/account
+
+
+This directory includes contracts to build accounts for ERC-4337. These include:
+
+* ERC7579Executor: An executor module that enables executing calls from accounts where the it’s installed.
+* ERC7579DelayedExecutor: An executor module that adds a delay before executing an account operation.
+* ERC7579SelectorExecutor: An executor module that restricts execution to specific function selectors.
+* ERC7579Validator: Abstract validator module for ERC-7579 accounts that provides base implementation for signature validation.
+* ERC7579Signature: Implementation of ERC7579Validator using ERC-7913 signature verification for address-less cryptographic keys and account signatures.
+* ERC7579Multisig: An extension of ERC7579Validator that enables validation using ERC-7913 signer keys.
+* ERC7579MultisigWeighted: An extension of ERC7579Multisig that allows different weights to be assigned to signers.
+* ERC7579MultisigConfirmation: An extension of ERC7579Multisig that requires each signer to provide a confirmation signature.
+* ERC7579MultisigStorage: An extension of ERC7579Multisig that allows storing presigned approvals in storage.
+* PaymasterCore: An ERC-4337 paymaster implementation that includes the core logic to validate and pay for user operations.
+* PaymasterERC20: A paymaster that allows users to pay for user operations using ERC-20 tokens.
+* PaymasterERC20Guarantor: A paymaster that enables third parties to guarantee user operations by pre-funding gas costs, with the option for users to repay or for guarantors to absorb the cost.
+* PaymasterERC721Owner: A paymaster that allows users to pay for user operations based on ERC-721 ownership.
+* PaymasterSigner: A paymaster that allows users to pay for user operations using an authorized signature.
+
+## Modules
+
+### Executors
+
+ERC7579Executor
+
+ERC7579DelayedExecutor
+
+ERC7579SelectorExecutor
+
+### Validators
+
+ERC7579Validator
+
+ERC7579Signature
+
+ERC7579Multisig
+
+ERC7579MultisigWeighted
+
+ERC7579MultisigConfirmation
+
+ERC7579MultisigStorage
+
+## Paymaster
+
+PaymasterCore
+
+PaymasterERC20
+
+PaymasterERC20Guarantor
+
+PaymasterERC721Owner
+
+PaymasterSigner
diff --git a/contracts/account/modules/ERC7579DelayedExecutor.sol b/contracts/account/modules/ERC7579DelayedExecutor.sol
index e800fa79..e7942a21 100644
--- a/contracts/account/modules/ERC7579DelayedExecutor.sol
+++ b/contracts/account/modules/ERC7579DelayedExecutor.sol
@@ -9,7 +9,7 @@ import {ERC7579Executor} from "./ERC7579Executor.sol";
* @dev Extension of {ERC7579Executor} that allows scheduling and executing delayed operations
* with expiration. This module enables time-delayed execution patterns for smart accounts.
*
- * ==== Operation Lifecycle
+ * #### Operation Lifecycle
*
* 1. Scheduling: Operations are scheduled via {schedule} with a specified delay period.
* The delay period is set during {onInstall} and can be customized via {setDelay}. Each
@@ -24,7 +24,7 @@ import {ERC7579Executor} from "./ERC7579Executor.sol";
* executable. If an operation is not executed within the expiration period, it becomes `Expired`
* and can't be executed. Expired operations must be rescheduled with a different salt.
*
- * ==== Delay Management
+ * #### Delay Management
*
* Accounts can set their own delay periods during installation or via {setDelay}.
* The delay period is enforced even between installas and uninstalls to prevent
@@ -32,14 +32,16 @@ import {ERC7579Executor} from "./ERC7579Executor.sol";
* after a transition period defined by the current delay or {minSetback}, whichever
* is longer.
*
- * ==== Authorization
+ * #### Authorization
*
* Authorization for scheduling and canceling operations is controlled through the {_validateSchedule}
* and {_validateCancel} functions. These functions can be overridden to implement custom
* authorization logic, such as requiring specific signers or roles.
*
- * TIP: Use {_scheduleAt} to schedule operations at a specific points in time. This is
+ *
+ * Use {_scheduleAt} to schedule operations at a specific points in time. This is
* useful to pre-schedule operations for non-deployed accounts (e.g. subscriptions).
+ *
*/
abstract contract ERC7579DelayedExecutor is ERC7579Executor {
using Time for *;
@@ -92,7 +94,9 @@ abstract contract ERC7579DelayedExecutor is ERC7579Executor {
* @dev The current state of a operation is not the expected. The `expectedStates` is a bitmap with the
* bits enabled for each OperationState enum position counting from right to left. See {_encodeStateBitmap}.
*
- * NOTE: If `expectedState` is `bytes32(0)`, the operation is expected to not be in any state (i.e. not exist).
+ *
+ * If `expectedState` is `bytes32(0)`, the operation is expected to not be in any state (i.e. not exist).
+ *
*/
error ERC7579ExecutorUnexpectedOperationState(
bytes32 operationId,
@@ -250,14 +254,18 @@ abstract contract ERC7579DelayedExecutor is ERC7579Executor {
* @dev Cleans up the {getDelay} and {getExpiration} values by scheduling them to `0`
* and respecting the previous delay and expiration values.
*
- * IMPORTANT: This function does not clean up scheduled operations. This means operations
+ *
+ * This function does not clean up scheduled operations. This means operations
* could potentially be re-executed if the module is reinstalled later. This is a deliberate
* design choice for efficiency, but module implementations may want to override this behavior
* to clear scheduled operations during uninstallation for their specific use cases.
+ *
*
- * NOTE: Calling this function directly will remove the expiration ({getExpiration}) value and
+ *
+ * Calling this function directly will remove the expiration ({getExpiration}) value and
* will schedule a reset of the delay ({getDelay}) to `0` for the account. Reinstalling the
* module will not immediately reset the delay if the delay reset hasn't taken effect yet.
+ *
*/
function onUninstall(bytes calldata) public virtual {
_config[msg.sender].installed = false;
@@ -268,9 +276,11 @@ abstract contract ERC7579DelayedExecutor is ERC7579Executor {
/**
* @dev Returns `data` as the execution calldata. See {ERC7579Executor-_execute}.
*
- * NOTE: This function relies on the operation state validation in {_execute} for
+ *
+ * This function relies on the operation state validation in {_execute} for
* authorization. Extensions of this module should override this function to implement
* additional validation logic if needed.
+ *
*/
function _validateExecution(
address /* account */,
diff --git a/contracts/account/modules/ERC7579Executor.sol b/contracts/account/modules/ERC7579Executor.sol
index b1201d07..5aa44aee 100644
--- a/contracts/account/modules/ERC7579Executor.sol
+++ b/contracts/account/modules/ERC7579Executor.sol
@@ -16,9 +16,11 @@ import {
* can be executed with custom rules by implementing the {_validateExecution} function in
* derived contracts.
*
- * TIP: This is a simplified executor that directly executes operations without delay or expiration
+ *
+ * This is a simplified executor that directly executes operations without delay or expiration
* mechanisms. For a more advanced implementation with time-delayed execution patterns and
* security features, see {ERC7579DelayedExecutor}.
+ *
*/
abstract contract ERC7579Executor is IERC7579Module {
/// @dev Emitted when an operation is executed.
@@ -67,9 +69,11 @@ abstract contract ERC7579Executor is IERC7579Module {
* }
*```
*
- * TIP: Pack extra data in the `data` arguments (e.g. a signature) to be used in the
+ *
+ * Pack extra data in the `data` arguments (e.g. a signature) to be used in the
* validation process. Calldata can be sliced to extract it and return only the
* execution calldata.
+ *
*/
function _validateExecution(
address account,
diff --git a/contracts/account/modules/ERC7579Multisig.sol b/contracts/account/modules/ERC7579Multisig.sol
index 7efc8dfd..2aa8ddd8 100644
--- a/contracts/account/modules/ERC7579Multisig.sol
+++ b/contracts/account/modules/ERC7579Multisig.sol
@@ -62,8 +62,10 @@ abstract contract ERC7579Multisig is ERC7579Validator {
* If no signers or threshold are provided, the multisignature functionality will be
* disabled until they are added later.
*
- * NOTE: An account can only call onInstall once. If called directly by the account,
+ *
+ * An account can only call onInstall once. If called directly by the account,
* the signer will be set to the provided data. Future installations will behave as a no-op.
+ *
*/
function onInstall(bytes calldata initData) public virtual {
if (initData.length > 32 && getSignerCount(msg.sender) == 0) {
@@ -80,8 +82,10 @@ abstract contract ERC7579Multisig is ERC7579Validator {
*
* See {ERC7579DelayedExecutor-onUninstall}.
*
- * WARNING: This function has unbounded gas costs and may become uncallable if the set grows too large.
+ *
+ * This function has unbounded gas costs and may become uncallable if the set grows too large.
* See {EnumerableSet-clear}.
+ *
*/
function onUninstall(bytes calldata /* data */) public virtual {
_signersSetByAccount[msg.sender].clear();
@@ -93,9 +97,11 @@ abstract contract ERC7579Multisig is ERC7579Validator {
*
* Using `start = 0` and `end = type(uint64).max` will return the entire set of signers.
*
- * WARNING: Depending on the `start` and `end`, this operation can copy a large amount of data to memory, which
+ *
+ * Depending on the `start` and `end`, this operation can copy a large amount of data to memory, which
* can be expensive. This is designed for view accessors queried without gas fees. Using it in state-changing
* functions may become uncallable if the slice grows too large.
+ *
*/
function getSigners(address account, uint64 start, uint64 end) public view virtual returns (bytes[] memory) {
return _signersSetByAccount[account].values(start, end);
diff --git a/contracts/account/modules/ERC7579MultisigConfirmation.sol b/contracts/account/modules/ERC7579MultisigConfirmation.sol
index 0cb436df..7c5235d0 100644
--- a/contracts/account/modules/ERC7579MultisigConfirmation.sol
+++ b/contracts/account/modules/ERC7579MultisigConfirmation.sol
@@ -13,8 +13,10 @@ import {ERC7579Multisig} from "./ERC7579Multisig.sol";
* multisig by requiring each new signer to provide a valid signature confirming their
* consent to be added. Each signer must sign an EIP-712 message to confirm their addition.
*
- * TIP: Use this module to ensure that all guardians in a social recovery or multisig setup have
+ *
+ * Use this module to ensure that all guardians in a social recovery or multisig setup have
* explicitly agreed to their roles.
+ *
*/
abstract contract ERC7579MultisigConfirmation is ERC7579Multisig, EIP712 {
bytes32 private constant MULTISIG_CONFIRMATION =
diff --git a/contracts/account/modules/ERC7579MultisigStorage.sol b/contracts/account/modules/ERC7579MultisigStorage.sol
index 728354ee..937c3dd7 100644
--- a/contracts/account/modules/ERC7579MultisigStorage.sol
+++ b/contracts/account/modules/ERC7579MultisigStorage.sol
@@ -35,8 +35,10 @@ abstract contract ERC7579MultisigStorage is ERC7579Multisig {
* Emits {ERC7579MultisigStoragePresigned} if the signature is valid and the hash is not already
* signed, otherwise acts as a no-op.
*
- * NOTE: Does not check if the signer is authorized for the account. Valid signatures from
+ *
+ * Does not check if the signer is authorized for the account. Valid signatures from
* invalid signers won't be executable. See {_validateSignatures} for more details.
+ *
*/
function presign(address account, bytes calldata signer, bytes32 hash, bytes calldata signature) public virtual {
if (!presigned(account, signer, hash) && signer.isValidSignatureNow(hash, signature)) {
diff --git a/contracts/account/modules/ERC7579MultisigWeighted.sol b/contracts/account/modules/ERC7579MultisigWeighted.sol
index 679b3ab7..012902e6 100644
--- a/contracts/account/modules/ERC7579MultisigWeighted.sol
+++ b/contracts/account/modules/ERC7579MultisigWeighted.sol
@@ -19,9 +19,11 @@ import {ERC7579Multisig} from "./ERC7579Multisig.sol";
* a total weight of 10, with 3 guardians weighted as 5, 3, and 2), and then execute them
* after the time delay has passed.
*
- * IMPORTANT: When setting a threshold value, ensure it matches the scale used for signer weights.
+ *
+ * When setting a threshold value, ensure it matches the scale used for signer weights.
* For example, if signers have weights like 1, 2, or 3, then a threshold of 4 would require
* signatures with a total weight of at least 4 (e.g., one with weight 1 and one with weight 3).
+ *
*/
abstract contract ERC7579MultisigWeighted is ERC7579Multisig {
using EnumerableSet for EnumerableSet.BytesSet;
@@ -36,8 +38,10 @@ abstract contract ERC7579MultisigWeighted is ERC7579Multisig {
/**
* @dev Emitted when a signer's weight is changed.
*
- * NOTE: Not emitted in {_addSigners} or {_removeSigners}. Indexers must rely on {ERC7913SignerAdded}
+ *
+ * Not emitted in {_addSigners} or {_removeSigners}. Indexers must rely on {ERC7913SignerAdded}
* and {ERC7913SignerRemoved} to index a default weight of 1. See {signerWeight}.
+ *
*/
event ERC7579MultisigWeightChanged(address indexed account, bytes indexed signer, uint64 weight);
@@ -57,8 +61,10 @@ abstract contract ERC7579MultisigWeighted is ERC7579Multisig {
*
* If weights are not provided but signers are, all signers default to weight 1.
*
- * NOTE: An account can only call onInstall once. If called directly by the account,
+ *
+ * An account can only call onInstall once. If called directly by the account,
* the signer will be set to the provided data. Future installations will behave as a no-op.
+ *
*/
function onInstall(bytes calldata initData) public virtual override {
bool installed = getSignerCount(msg.sender) > 0;
@@ -198,10 +204,12 @@ abstract contract ERC7579MultisigWeighted is ERC7579Multisig {
/**
* @dev Override to validate threshold against total weight instead of signer count.
*
- * NOTE: This function intentionally does not call `super._validateReachableThreshold` because the base implementation
+ *
+ * This function intentionally does not call `super._validateReachableThreshold` because the base implementation
* assumes each signer has a weight of 1, which is a subset of this weighted implementation. Consider that multiple
* implementations of this function may exist in the contract, so important side effects may be missed
* depending on the linearization order.
+ *
*/
function _validateReachableThreshold(address account) internal view virtual override {
uint64 weight = totalWeight(account);
@@ -213,8 +221,10 @@ abstract contract ERC7579MultisigWeighted is ERC7579Multisig {
* @dev Validates that the total weight of signers meets the {threshold} requirement.
* Overrides the base implementation to use weights instead of count.
*
- * NOTE: This function intentionally does not call `super._validateThreshold` because the base implementation
+ *
+ * This function intentionally does not call `super._validateThreshold` because the base implementation
* assumes each signer has a weight of 1, which is incompatible with this weighted implementation.
+ *
*/
function _validateThreshold(
address account,
diff --git a/contracts/account/modules/ERC7579SelectorExecutor.sol b/contracts/account/modules/ERC7579SelectorExecutor.sol
index 53d60380..883099b5 100644
--- a/contracts/account/modules/ERC7579SelectorExecutor.sol
+++ b/contracts/account/modules/ERC7579SelectorExecutor.sol
@@ -37,8 +37,10 @@ abstract contract ERC7579SelectorExecutor is ERC7579Executor {
/**
* @dev Returns the set of authorized selectors for the specified account.
*
- * WARNING: This operation copies the entire selectors set to memory, which
+ *
+ * This operation copies the entire selectors set to memory, which
* can be expensive or may result in unbounded computation.
+ *
*/
function selectors(address account) public view virtual returns (bytes4[] memory) {
bytes32[] memory bytes32Selectors = _authorizedSelectors[account].values();
@@ -64,8 +66,10 @@ abstract contract ERC7579SelectorExecutor is ERC7579Executor {
* @dev Cleans up module's configuration when uninstalled from an account.
* Clears all selectors.
*
- * WARNING: This function has unbounded gas costs and may become uncallable if the set grows too large.
+ *
+ * This function has unbounded gas costs and may become uncallable if the set grows too large.
* See {EnumerableSetExtended-clear}.
+ *
*/
function onUninstall(bytes calldata /* data */) public virtual override {
_authorizedSelectors[msg.sender].clear();
diff --git a/contracts/account/modules/ERC7579Signature.sol b/contracts/account/modules/ERC7579Signature.sol
index 36d0b5e9..6fd52e8d 100644
--- a/contracts/account/modules/ERC7579Signature.sol
+++ b/contracts/account/modules/ERC7579Signature.sol
@@ -35,8 +35,10 @@ contract ERC7579Signature is ERC7579Validator {
/**
* @dev See {IERC7579Module-onInstall}.
*
- * NOTE: An account can only call onInstall once. If called directly by the account,
+ *
+ * An account can only call onInstall once. If called directly by the account,
* the signer will be set to the provided data. Future installations will behave as a no-op.
+ *
*/
function onInstall(bytes calldata data) public virtual {
if (signer(msg.sender).length == 0) {
@@ -47,9 +49,11 @@ contract ERC7579Signature is ERC7579Validator {
/**
* @dev See {IERC7579Module-onUninstall}.
*
- * WARNING: The signer's key will be removed if the account calls this function, potentially
+ *
+ * The signer's key will be removed if the account calls this function, potentially
* making the account unusable. As an account operator, make sure to uninstall to a predefined path
* in your account that properly handles side effects of uninstallation. See {AccountERC7579-uninstallModule}.
+ *
*/
function onUninstall(bytes calldata) public virtual {
_setSigner(msg.sender, "");
diff --git a/contracts/account/modules/ERC7579Validator.sol b/contracts/account/modules/ERC7579Validator.sol
index 0d942113..f985d0cb 100644
--- a/contracts/account/modules/ERC7579Validator.sol
+++ b/contracts/account/modules/ERC7579Validator.sol
@@ -94,8 +94,10 @@ abstract contract ERC7579Validator is IERC7579Module, IERC7579Validator {
/**
* @dev Validation algorithm.
*
- * WARNING: Validation is a critical security function. Implementations must carefully
+ *
+ * Validation is a critical security function. Implementations must carefully
* handle cryptographic verification to prevent unauthorized access.
+ *
*/
function _rawERC7579Validation(
address account,
diff --git a/contracts/account/paymaster/PaymasterCore.sol b/contracts/account/paymaster/PaymasterCore.sol
index 58e538ab..3866602f 100644
--- a/contracts/account/paymaster/PaymasterCore.sol
+++ b/contracts/account/paymaster/PaymasterCore.sol
@@ -18,8 +18,10 @@ import {IEntryPoint, IPaymaster, PackedUserOperation} from "@openzeppelin/contra
* * Deposits are used to pay for user operations.
* * Stakes are used to guarantee the paymaster's reputation and obtain more flexibility in accessing storage.
*
- * NOTE: See [Paymaster's unstaked reputation rules](https://eips.ethereum.org/EIPS/eip-7562#unstaked-paymasters-reputation-rules)
+ *
+ * See [Paymaster's unstaked reputation rules](https://eips.ethereum.org/EIPS/eip-7562#unstaked-paymasters-reputation-rules)
* for more details on the paymaster's storage access limitations.
+ *
*/
abstract contract PaymasterCore is IPaymaster {
/// @dev Unauthorized call to the paymaster.
@@ -80,8 +82,10 @@ abstract contract PaymasterCore is IPaymaster {
* It receives the `context` returned by `_validatePaymasterUserOp`. Function is not called if no context
* is returned by {validatePaymasterUserOp}.
*
- * NOTE: The `actualUserOpFeePerGas` is not `tx.gasprice`. A user operation can be bundled with other transactions
+ *
+ * The `actualUserOpFeePerGas` is not `tx.gasprice`. A user operation can be bundled with other transactions
* making the gas price of the user operation to differ.
+ *
*/
function _postOp(
PostOpMode /* mode */,
@@ -126,7 +130,7 @@ abstract contract PaymasterCore is IPaymaster {
/**
* @dev Checks whether `msg.sender` withdraw funds stake or deposit from the entrypoint on paymaster's behalf.
*
- * Use of an https://docs.openzeppelin.com/contracts/5.x/access-control[access control]
+ * Use of an [access control](https://docs.openzeppelin.com/contracts/5.x/access-control)
* modifier such as {Ownable-onlyOwner} is recommended.
*
* ```solidity
diff --git a/contracts/account/paymaster/PaymasterERC20.sol b/contracts/account/paymaster/PaymasterERC20.sol
index 4e8c490b..a0d2b88e 100644
--- a/contracts/account/paymaster/PaymasterERC20.sol
+++ b/contracts/account/paymaster/PaymasterERC20.sol
@@ -89,9 +89,11 @@ abstract contract PaymasterERC20 is PaymasterCore {
*
* Returns a `prefundContext` that's passed to the {_postOp} function through its `context` return value.
*
- * NOTE: Consider not reverting if the prefund fails when overriding this function. This is to avoid reverting
+ *
+ * Consider not reverting if the prefund fails when overriding this function. This is to avoid reverting
* during the validation phase of the user operation, which may penalize the paymaster's reputation according
* to ERC-7562 validation rules.
+ *
*/
function _prefund(
PackedUserOperation calldata userOp,
@@ -111,9 +113,11 @@ abstract contract PaymasterERC20 is PaymasterCore {
*
* Reverts with {PaymasterERC20FailedRefund} if the refund fails.
*
- * IMPORTANT: This function may revert after the user operation has been executed without
+ *
+ * This function may revert after the user operation has been executed without
* reverting the user operation itself. Consider implementing a mechanism to handle
* this case gracefully.
+ *
*/
function _postOp(
PostOpMode /* mode */,
@@ -172,7 +176,7 @@ abstract contract PaymasterERC20 is PaymasterCore {
* * `token`: Address of the ERC-20 token used for payment to the paymaster.
* * `tokenPrice`: Price of the token in native currency, scaled by `_tokenPriceDenominator()`.
*
- * ==== Calculating the token price
+ * #### Calculating the token price
*
* Given gas fees are paid in native currency, developers can use the `ERC20 price unit / native price unit` ratio to
* calculate the price of an ERC20 token price in native currency. However, the token may have a different number of decimals
diff --git a/contracts/account/paymaster/PaymasterERC20Guarantor.sol b/contracts/account/paymaster/PaymasterERC20Guarantor.sol
index 86d3b055..4fca0d66 100644
--- a/contracts/account/paymaster/PaymasterERC20Guarantor.sol
+++ b/contracts/account/paymaster/PaymasterERC20Guarantor.sol
@@ -73,8 +73,10 @@ abstract contract PaymasterERC20Guarantor is PaymasterERC20 {
* If the operation was guaranteed, it attempts to get repayment from the user first and then refunds the guarantor.
* Otherwise, fallback to {PaymasterERC20-_refund}.
*
- * NOTE: For guaranteed user operations where the user paid the `actualGasCost` back, this function
+ *
+ * For guaranteed user operations where the user paid the `actualGasCost` back, this function
* doesn't call `super._refund`. Consider whether there are side effects in the parent contract that need to be executed.
+ *
*/
function _refund(
IERC20 token,
@@ -109,8 +111,10 @@ abstract contract PaymasterERC20Guarantor is PaymasterERC20 {
/**
* @dev Fetches the guarantor address and validation data from the user operation.
*
- * NOTE: Return `address(0)` to disable the guarantor feature. If supported, ensure
+ *
+ * Return `address(0)` to disable the guarantor feature. If supported, ensure
* explicit consent (e.g., signature verification) to prevent unauthorized use.
+ *
*/
function _fetchGuarantor(PackedUserOperation calldata userOp) internal view virtual returns (address guarantor);
diff --git a/contracts/account/paymaster/PaymasterERC721Owner.sol b/contracts/account/paymaster/PaymasterERC721Owner.sol
index 0b4449f3..cd868db0 100644
--- a/contracts/account/paymaster/PaymasterERC721Owner.sol
+++ b/contracts/account/paymaster/PaymasterERC721Owner.sol
@@ -37,8 +37,10 @@ abstract contract PaymasterERC721Owner is PaymasterCore {
* @dev Internal validation of whether the paymaster is willing to pay for the user operation.
* Returns the context to be passed to postOp and the validation data.
*
- * NOTE: The default `context` is `bytes(0)`. Developers that add a context when overriding this function MUST
+ *
+ * The default `context` is `bytes(0)`. Developers that add a context when overriding this function MUST
* also override {_postOp} to process the context passed along.
+ *
*/
function _validatePaymasterUserOp(
PackedUserOperation calldata userOp,
diff --git a/contracts/account/paymaster/PaymasterSigner.sol b/contracts/account/paymaster/PaymasterSigner.sol
index d1108935..faaa21ce 100644
--- a/contracts/account/paymaster/PaymasterSigner.sol
+++ b/contracts/account/paymaster/PaymasterSigner.sol
@@ -61,8 +61,10 @@ abstract contract PaymasterSigner is AbstractSigner, EIP712, PaymasterCore {
* @dev Internal validation of whether the paymaster is willing to pay for the user operation.
* Returns the context to be passed to postOp and the validation data.
*
- * NOTE: The `context` returned is `bytes(0)`. Developers overriding this function MUST
+ *
+ * The `context` returned is `bytes(0)`. Developers overriding this function MUST
* override {_postOp} to process the context passed along.
+ *
*/
function _validatePaymasterUserOp(
PackedUserOperation calldata userOp,
diff --git a/contracts/crosschain/ERC7786OpenBridge.sol b/contracts/crosschain/ERC7786OpenBridge.sol
index aa9a58f2..03b92992 100644
--- a/contracts/crosschain/ERC7786OpenBridge.sol
+++ b/contracts/crosschain/ERC7786OpenBridge.sol
@@ -168,9 +168,11 @@ contract ERC7786OpenBridge is IERC7786GatewaySource, IERC7786Receiver, Ownable,
* * {ExecutionSuccess} when a message is successfully delivered to the receiver.
* * {ExecutionFailed} when a message delivery to the receiver reverted (for example because of OOG error).
*
- * NOTE: interface requires this function to be payable. Even if we don't expect any value, a gateway may pass
+ *
+ * interface requires this function to be payable. Even if we don't expect any value, a gateway may pass
* some value for unknown reason. In that case we want to register this gateway having delivered the message and
* not revert. Any value accrued that way can be recovered by the admin using the {sweep} function.
+ *
*/
// slither-disable-next-line reentrancy-no-eth
function receiveMessage(
diff --git a/contracts/crosschain/README.adoc b/contracts/crosschain/README.adoc
deleted file mode 100644
index 21ecf6aa..00000000
--- a/contracts/crosschain/README.adoc
+++ /dev/null
@@ -1,27 +0,0 @@
-= Crosschain
-
-[.readme-notice]
-NOTE: This document is better viewed at https://docs.openzeppelin.com/community-contracts/api/crosschain
-
-Gateways are contracts that enable cross-chain communication. These can either be a message source or a destination according to ERC-7786.
-
- * {ERC7786Receiver}: ERC-7786 cross-chain message receiver.
- * {ERC7786OpenBridge}: ERC-7786 "N out of M" gateway. Sends a message through M gateways and executes on the destination if N received it.
-
-Developers can access interoperability protocols through gateway adapters. The library includes the following gateway adapters:
-
- * {AxelarGatewayAdapter}: ERC-7786 gateway adapter for Axelar.
-
-== Gateways
-
-{{ERC7786OpenBridge}}
-
-== Clients
-
-{{ERC7786Receiver}}
-
-== Adapters
-
-=== Axelar
-
-{{AxelarGatewayAdapter}}
diff --git a/contracts/crosschain/README.mdx b/contracts/crosschain/README.mdx
new file mode 100644
index 00000000..86fad9e9
--- /dev/null
+++ b/contracts/crosschain/README.mdx
@@ -0,0 +1,30 @@
+---
+title: Crosschain
+---
+
+
+This document is better viewed at https://docs.openzeppelin.com/community-contracts/api/crosschain
+
+
+Gateways are contracts that enable cross-chain communication. These can either be a message source or a destination according to ERC-7786.
+
+* ERC7786Receiver: ERC-7786 cross-chain message receiver.
+* ERC7786OpenBridge: ERC-7786 "N out of M" gateway. Sends a message through M gateways and executes on the destination if N received it.
+
+Developers can access interoperability protocols through gateway adapters. The library includes the following gateway adapters:
+
+* AxelarGatewayAdapter: ERC-7786 gateway adapter for Axelar.
+
+## Gateways
+
+ERC7786OpenBridge
+
+## Clients
+
+ERC7786Receiver
+
+## Adapters
+
+### Axelar
+
+AxelarGatewayAdapter
diff --git a/contracts/crosschain/axelar/AxelarGatewayAdapter.sol b/contracts/crosschain/axelar/AxelarGatewayAdapter.sol
index 7a8895ec..c571874b 100644
--- a/contracts/crosschain/axelar/AxelarGatewayAdapter.sol
+++ b/contracts/crosschain/axelar/AxelarGatewayAdapter.sol
@@ -16,11 +16,13 @@ import {IERC7786Receiver} from "../../interfaces/IERC7786.sol";
* The contract implements AxelarExecutable's {_execute} function to execute the message, converting Axelar's native
* workflow into the standard ERC-7786.
*
- * NOTE: While both ERC-7786 and Axelar do support non-evm chains, this adaptor does not. This limitation comes from
+ *
+ * While both ERC-7786 and Axelar do support non-evm chains, this adaptor does not. This limitation comes from
* the translation of the ERC-7930 interoperable address (binary objects -- bytes) to strings. This is necessary
* because Axelar uses string to represent addresses. For EVM network, this adapter uses a checksum hex string
* representation. Other networks would require a different encoding. Ideally we would have a single encoding for all
* networks (could be base58, base64, ...) but Axelar doesn't support that.
+ *
*/
// slither-disable-next-line locked-ether
contract AxelarGatewayAdapter is IERC7786GatewaySource, Ownable, AxelarExecutable {
diff --git a/contracts/governance/README.adoc b/contracts/governance/README.adoc
deleted file mode 100644
index f8aa55da..00000000
--- a/contracts/governance/README.adoc
+++ /dev/null
@@ -1,12 +0,0 @@
-= Governance
-
-[.readme-notice]
-NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/governance
-
-This directory includes extensions and utilities for on-chain governance.
-
-* {TimelockControllerEnumerable}: Extension of OpenZeppelin's TimelockController with enumerable operations support.
-
-== Timelock
-
-{{TimelockControllerEnumerable}}
\ No newline at end of file
diff --git a/contracts/governance/README.mdx b/contracts/governance/README.mdx
new file mode 100644
index 00000000..d539b399
--- /dev/null
+++ b/contracts/governance/README.mdx
@@ -0,0 +1,15 @@
+---
+title: Governance
+---
+
+
+This document is better viewed at https://docs.openzeppelin.com/contracts/api/governance
+
+
+This directory includes extensions and utilities for on-chain governance.
+
+* TimelockControllerEnumerable: Extension of OpenZeppelin’s TimelockController with enumerable operations support.
+
+## Timelock
+
+TimelockControllerEnumerable
diff --git a/contracts/governance/TimelockControllerEnumerable.sol b/contracts/governance/TimelockControllerEnumerable.sol
index 8088eccb..f3a16ae7 100644
--- a/contracts/governance/TimelockControllerEnumerable.sol
+++ b/contracts/governance/TimelockControllerEnumerable.sol
@@ -107,8 +107,10 @@ abstract contract TimelockControllerEnumerable is TimelockController {
}
/// @dev Return all scheduled operations
- /// WARNING: This is designed for view accessors queried without gas fees. Using it in state-changing
+ ///
+ /// This is designed for view accessors queried without gas fees. Using it in state-changing
/// functions may become uncallable if the list grows too large.
+ ///
function operations() public view returns (Operation[] memory operations_) {
return operations(0, _operationsIdSet.length());
}
@@ -117,8 +119,10 @@ abstract contract TimelockControllerEnumerable is TimelockController {
/// @param start The start index
/// @param end The end index
/// @return operations_ The operations
- /// WARNING: This is designed for view accessors queried without gas fees. Using it in state-changing
+ ///
+ /// This is designed for view accessors queried without gas fees. Using it in state-changing
/// functions may become uncallable if the list grows too large.
+ ///
function operations(uint256 start, uint256 end) public view returns (Operation[] memory operations_) {
if (start > end || start >= _operationsIdSet.length()) {
revert InvalidIndexRange(start, end);
@@ -155,8 +159,10 @@ abstract contract TimelockControllerEnumerable is TimelockController {
}
/// @dev Return all scheduled operation batches
- /// WARNING: This is designed for view accessors queried without gas fees. Using it in state-changing
+ ///
+ /// This is designed for view accessors queried without gas fees. Using it in state-changing
/// functions may become uncallable if the list grows too large.
+ ///
function operationsBatch() public view returns (OperationBatch[] memory operationsBatch_) {
return operationsBatch(0, _operationsBatchIdSet.length());
}
@@ -165,8 +171,10 @@ abstract contract TimelockControllerEnumerable is TimelockController {
/// @param start The start index
/// @param end The end index
/// @return operationsBatch_ The operationsBatch
- /// WARNING: This is designed for view accessors queried without gas fees. Using it in state-changing
+ ///
+ /// This is designed for view accessors queried without gas fees. Using it in state-changing
/// functions may become uncallable if the list grows too large.
+ ///
function operationsBatch(
uint256 start,
uint256 end
diff --git a/contracts/interfaces/IERC7969.sol b/contracts/interfaces/IERC7969.sol
index 23544d86..0eea76b0 100644
--- a/contracts/interfaces/IERC7969.sol
+++ b/contracts/interfaces/IERC7969.sol
@@ -8,7 +8,9 @@ pragma solidity ^0.8.20;
* Domain owners can register their DKIM public key hashes and third parties can verify their validity
* The interface enables email-based account abstraction and secure account recovery mechanisms.
*
- * NOTE: The ERC-165 identifier for this interface is `0xdee3d600`.
+ *
+ * The ERC-165 identifier for this interface is `0xdee3d600`.
+ *
*/
interface IDKIMRegistry {
/// @dev Emitted when a new DKIM public key hash is registered for a domain
diff --git a/contracts/interfaces/README.adoc b/contracts/interfaces/README.adoc
deleted file mode 100644
index f820626e..00000000
--- a/contracts/interfaces/README.adoc
+++ /dev/null
@@ -1,28 +0,0 @@
-= Interfaces
-
-[.readme-notice]
-NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/interfaces
-
-== List of standardized interfaces
-
-These interfaces are available as `.sol` files. These are useful to interact with third party contracts that implement them.
-
-- {IERC7786GatewaySource}, {IERC7786Receiver}
-- {IERC7802}
-- {IERC7821}
-- {IERC7913SignatureVerifier}
-- {IERC7943}
-
-== Detailed ABI
-
-{{IERC7786GatewaySource}}
-
-{{IERC7786Receiver}}
-
-{{IERC7802}}
-
-{{IERC7821}}
-
-{{IERC7913SignatureVerifier}}
-
-{{IERC7943}}
diff --git a/contracts/interfaces/README.mdx b/contracts/interfaces/README.mdx
new file mode 100644
index 00000000..640f1a1a
--- /dev/null
+++ b/contracts/interfaces/README.mdx
@@ -0,0 +1,31 @@
+---
+title: Interfaces
+---
+
+
+This document is better viewed at https://docs.openzeppelin.com/contracts/api/interfaces
+
+
+## List of standardized interfaces
+
+These interfaces are available as `.sol` files. These are useful to interact with third party contracts that implement them.
+
+* IERC7786GatewaySource, IERC7786Receiver
+* IERC7802
+* IERC7821
+* IERC7913SignatureVerifier
+* IERC7943
+
+## Detailed ABI
+
+IERC7786GatewaySource
+
+IERC7786Receiver
+
+IERC7802
+
+IERC7821
+
+IERC7913SignatureVerifier
+
+IERC7943
diff --git a/contracts/mocks/account/paymaster/PaymasterERC20Mock.sol b/contracts/mocks/account/paymaster/PaymasterERC20Mock.sol
index 22a39da0..7ad52529 100644
--- a/contracts/mocks/account/paymaster/PaymasterERC20Mock.sol
+++ b/contracts/mocks/account/paymaster/PaymasterERC20Mock.sol
@@ -10,7 +10,8 @@ import {PaymasterERC20, IERC20} from "../../../account/paymaster/PaymasterERC20.
import {PaymasterERC20Guarantor} from "../../../account/paymaster/PaymasterERC20Guarantor.sol";
/**
- * NOTE: struct or the expected paymaster data is:
+ *
+ * struct or the expected paymaster data is:
* * [0x00:0x14 ] token (IERC20)
* * [0x14:0x1a ] validAfter (uint48)
* * [0x1a:0x20 ] validUntil (uint48)
@@ -18,6 +19,7 @@ import {PaymasterERC20Guarantor} from "../../../account/paymaster/PaymasterERC20
* * [0x40:0x54 ] oracle (address)
* * [0x54:0x56 ] oracleSignatureLength (uint16)
* * [0x56:0x56+oracleSignatureLength] oracleSignature (bytes)
+ *
*/
abstract contract PaymasterERC20Mock is EIP712, PaymasterERC20, AccessControl {
using ERC4337Utils for *;
@@ -61,10 +63,12 @@ abstract contract PaymasterERC20Mock is EIP712, PaymasterERC20, AccessControl {
}
/**
- * NOTE: struct or the expected guarantor data is (starts at 0x56+oracleSignatureLength):
+ *
+ * struct or the expected guarantor data is (starts at 0x56+oracleSignatureLength):
* * [0x00:0x14 ] guarantor (address) (optional: 0 if no guarantor)
* * [0x14:0x16 ] guarantorSignatureLength (uint16)
* * [0x16+guarantorSignatureLength ] guarantorSignature (bytes)
+ *
*/
abstract contract PaymasterERC20GuarantorMock is PaymasterERC20Mock, PaymasterERC20Guarantor {
using ERC4337Utils for *;
diff --git a/contracts/proxy/HybridProxy.sol b/contracts/proxy/HybridProxy.sol
index d2db97e0..3b520669 100644
--- a/contracts/proxy/HybridProxy.sol
+++ b/contracts/proxy/HybridProxy.sol
@@ -15,9 +15,11 @@ import {Address} from "@openzeppelin/contracts/utils/Address.sol";
* upgrade mechanism that writes to the ERC-1967 implementation slot. Note that UUPSUpgradable includes security
* checks that are not compatible with this proxy design.
*
- * WARNING: The fallback mechanism relies on the implementation not to define the {IBeacon-implementation} function.
+ *
+ * The fallback mechanism relies on the implementation not to define the {IBeacon-implementation} function.
* Consider that if your implementation has this function, it'll be assumed as the beacon address, meaning that
* the returned address will be used as this proxy's implementation.
+ *
*/
contract HybridProxy is Proxy {
/**
@@ -34,9 +36,11 @@ contract HybridProxy is Proxy {
/**
* @dev Returns the current implementation address according to ERC-1967's implementation slot.
*
- * IMPORTANT: The way this function identifies whether the implementation is a beacon, is by checking
+ *
+ * The way this function identifies whether the implementation is a beacon, is by checking
* if it implements the {IBeacon-implementation} function. Consider that an actual implementation could
* define this function, mistakenly identifying it as a beacon.
+ *
*/
function _implementation() internal view override returns (address) {
address implementation = ERC1967Utils.getImplementation();
diff --git a/contracts/proxy/README.adoc b/contracts/proxy/README.adoc
deleted file mode 100644
index 61696541..00000000
--- a/contracts/proxy/README.adoc
+++ /dev/null
@@ -1,12 +0,0 @@
-= Proxy
-
-[.readme-notice]
-NOTE: This document is better viewed at https://docs.openzeppelin.com/community-contracts/proxy
-
-Variants of proxy patterns, which are contracts that allow to forward a call to an implementation contract by using `delegatecall`. This contracts include:
-
- * {HybridProxy}: An ERC-1967 proxy that uses the implementation slot as a beacon in a way that a user can upgrade to an implementation of their choice.
-
-== General
-
-{{HybridProxy}}
diff --git a/contracts/proxy/README.mdx b/contracts/proxy/README.mdx
new file mode 100644
index 00000000..cd8c163d
--- /dev/null
+++ b/contracts/proxy/README.mdx
@@ -0,0 +1,15 @@
+---
+title: Proxy
+---
+
+
+This document is better viewed at https://docs.openzeppelin.com/community-contracts/proxy
+
+
+Variants of proxy patterns, which are contracts that allow to forward a call to an implementation contract by using `delegatecall`. This contracts include:
+
+* HybridProxy: An ERC-1967 proxy that uses the implementation slot as a beacon in a way that a user can upgrade to an implementation of their choice.
+
+## General
+
+HybridProxy
diff --git a/contracts/token/ERC20/extensions/ERC20Allowlist.sol b/contracts/token/ERC20/extensions/ERC20Allowlist.sol
index 508fc028..4ed49f75 100644
--- a/contracts/token/ERC20/extensions/ERC20Allowlist.sol
+++ b/contracts/token/ERC20/extensions/ERC20Allowlist.sol
@@ -16,7 +16,9 @@ import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
* argument. Similarly, the account will be disallowed again if
* {_disallowUser} is called.
*
- * IMPORTANT: Deprecated. Use {ERC20Restricted} instead.
+ *
+ * Deprecated. Use {ERC20Restricted} instead.
+ *
*/
abstract contract ERC20Allowlist is ERC20 {
/**
diff --git a/contracts/token/ERC20/extensions/ERC20Blocklist.sol b/contracts/token/ERC20/extensions/ERC20Blocklist.sol
index 726e2cf4..a8ff9dec 100644
--- a/contracts/token/ERC20/extensions/ERC20Blocklist.sol
+++ b/contracts/token/ERC20/extensions/ERC20Blocklist.sol
@@ -16,7 +16,9 @@ import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
* argument. Similarly, the account will be unblocked again if
* {_unblockUser} is called.
*
- * IMPORTANT: Deprecated. Use {ERC20Restricted} instead.
+ *
+ * Deprecated. Use {ERC20Restricted} instead.
+ *
*/
abstract contract ERC20Blocklist is ERC20 {
/**
diff --git a/contracts/token/ERC20/extensions/ERC20Custodian.sol b/contracts/token/ERC20/extensions/ERC20Custodian.sol
index 8acbf85b..a9669d9e 100644
--- a/contracts/token/ERC20/extensions/ERC20Custodian.sol
+++ b/contracts/token/ERC20/extensions/ERC20Custodian.sol
@@ -17,7 +17,9 @@ import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
* to other entities to operate on its behalf if. The frozen balance
* can be reduced by calling {freeze} again with a lower amount.
*
- * IMPORTANT: Deprecated. Use {ERC20Freezable} instead.
+ *
+ * Deprecated. Use {ERC20Freezable} instead.
+ *
*/
abstract contract ERC20Custodian is ERC20 {
/**
diff --git a/contracts/token/ERC20/extensions/ERC20Restricted.sol b/contracts/token/ERC20/extensions/ERC20Restricted.sol
index 78b81183..bff6312f 100644
--- a/contracts/token/ERC20/extensions/ERC20Restricted.sol
+++ b/contracts/token/ERC20/extensions/ERC20Restricted.sol
@@ -6,7 +6,7 @@ import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
/**
* @dev Extension of {ERC20} that allows to implement user account transfer restrictions
- * through the {isUserAllowed} function. Inspired by https://eips.ethereum.org/EIPS/eip-7943[EIP-7943].
+ * through the {isUserAllowed} function. Inspired by [EIP-7943](https://eips.ethereum.org/EIPS/eip-7943).
*
* By default, each account has no explicit restriction. The {isUserAllowed} function acts as
* a blocklist. Developers can override {isUserAllowed} to check that `restriction == ALLOWED`
diff --git a/contracts/token/ERC20/extensions/ERC20uRWA.sol b/contracts/token/ERC20/extensions/ERC20uRWA.sol
index ab93aa34..8b4b3a14 100644
--- a/contracts/token/ERC20/extensions/ERC20uRWA.sol
+++ b/contracts/token/ERC20/extensions/ERC20uRWA.sol
@@ -9,7 +9,7 @@ import {ERC20Freezable} from "./ERC20Freezable.sol";
import {ERC20Restricted} from "./ERC20Restricted.sol";
/**
- * @dev Extension of {ERC20} according to https://eips.ethereum.org/EIPS/eip-7943[EIP-7943].
+ * @dev Extension of {ERC20} according to [EIP-7943](https://eips.ethereum.org/EIPS/eip-7943).
*
* Combines standard ERC-20 functionality with RWA-specific features like user restrictions,
* asset freezing, and forced asset transfers.
@@ -30,8 +30,10 @@ abstract contract ERC20uRWA is ERC20, ERC165, ERC20Freezable, ERC20Restricted, I
/**
* @dev See {IERC7943Fungible-canTransfer}.
*
- * CAUTION: This function is only meant for external use. Overriding it will not apply the new checks to
+ *
+ * This function is only meant for external use. Overriding it will not apply the new checks to
* the internal {_update} function. Consider overriding {_update} accordingly to keep both functions in sync.
+ *
*/
function canTransfer(address from, address to, uint256 amount) external view virtual returns (bool) {
return (amount <= available(from) && isUserAllowed(from) && isUserAllowed(to));
@@ -45,8 +47,10 @@ abstract contract ERC20uRWA is ERC20, ERC165, ERC20Freezable, ERC20Restricted, I
/**
* @dev See {IERC7943Fungible-setFrozenTokens}.
*
- * NOTE: The `amount` is capped to the balance of the `user` to ensure the {IERC7943Fungible-Frozen} event
+ *
+ * The `amount` is capped to the balance of the `user` to ensure the {IERC7943Fungible-Frozen} event
* emits values that consistently reflect the actual amount of tokens that are frozen.
+ *
*/
function setFrozenTokens(address user, uint256 amount) public virtual {
uint256 actualAmount = Math.min(amount, balanceOf(user));
@@ -60,10 +64,12 @@ abstract contract ERC20uRWA is ERC20, ERC165, ERC20Freezable, ERC20Restricted, I
* Bypasses the {ERC20Restricted} restrictions for the `from` address and adjusts the frozen balance
* to the new balance after the transfer.
*
- * NOTE: This function uses {_update} to perform the transfer, ensuring all standard ERC20
+ *
+ * This function uses {_update} to perform the transfer, ensuring all standard ERC20
* side effects (such as balance updates and events) are preserved. If you override {_update}
* to add additional restrictions or logic, those changes will also apply here.
* Consider overriding this function to bypass newer restrictions if needed.
+ *
*/
function forcedTransfer(address from, address to, uint256 amount) public virtual {
_checkEnforcer(from, to, amount);
diff --git a/contracts/token/ERC20/extensions/ERC4626Fees.sol b/contracts/token/ERC20/extensions/ERC4626Fees.sol
index 201ee8da..27964aaf 100644
--- a/contracts/token/ERC20/extensions/ERC4626Fees.sol
+++ b/contracts/token/ERC20/extensions/ERC4626Fees.sol
@@ -7,7 +7,7 @@ import {ERC4626} from "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.so
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
-/// @dev ERC-4626 vault with entry/exit fees expressed in https://en.wikipedia.org/wiki/Basis_point[basis point (bp)].
+/// @dev ERC-4626 vault with entry/exit fees expressed in [basis point (bp)](https://en.wikipedia.org/wiki/Basis_point).
abstract contract ERC4626Fees is ERC4626 {
using Math for uint256;
diff --git a/contracts/token/README.adoc b/contracts/token/README.adoc
deleted file mode 100644
index e98410d3..00000000
--- a/contracts/token/README.adoc
+++ /dev/null
@@ -1,29 +0,0 @@
-= Tokens
-
-[.readme-notice]
-NOTE: This document is better viewed at https://docs.openzeppelin.com/community-contracts/token
-
-Set of extensions and utilities for tokens (e.g ERC-20, ERC-721, ERC-1155) and derivated ERCs (e.g. ERC-4626, ERC-1363).
-
- * {OnTokenTransferAdapter}: Adapter of the ERC-1363 receiver interface to comply with Chainlink's 667 interface.
- * {ERC20Allowlist}: Extension of ERC20 with transfers and approvals that require users to be registered into an allowlist.
- * {ERC20Blocklist}: Extension of ERC20 with transfers and approvals that can be disabled by adding users into a blocklist.
- * {ERC20Collateral}: Oracle-agnostic extension of ERC20 that limits the total supply based on a collateral amount.
- * {ERC20Custodian}: Extension of ERC20 that implements an access-control agnostic approach to define a custodian that can freeze user's transfers and approvals.
- * {ERC4626Fees}: ERC4626 vault with fees on entry (deposit/mint) or exit (withdraw/redeem).
-
-== General
-
-{{OnTokenTransferAdapter}}
-
-== ERC20
-
-{{ERC20Allowlist}}
-
-{{ERC20Blocklist}}
-
-{{ERC20Collateral}}
-
-{{ERC20Custodian}}
-
-{{ERC4626Fees}}
diff --git a/contracts/token/README.mdx b/contracts/token/README.mdx
new file mode 100644
index 00000000..eb770c98
--- /dev/null
+++ b/contracts/token/README.mdx
@@ -0,0 +1,32 @@
+---
+title: Tokens
+---
+
+
+This document is better viewed at https://docs.openzeppelin.com/community-contracts/token
+
+
+Set of extensions and utilities for tokens (e.g ERC-20, ERC-721, ERC-1155) and derivated ERCs (e.g. ERC-4626, ERC-1363).
+
+* OnTokenTransferAdapter: Adapter of the ERC-1363 receiver interface to comply with Chainlink’s 667 interface.
+* ERC20Allowlist: Extension of ERC20 with transfers and approvals that require users to be registered into an allowlist.
+* ERC20Blocklist: Extension of ERC20 with transfers and approvals that can be disabled by adding users into a blocklist.
+* ERC20Collateral: Oracle-agnostic extension of ERC20 that limits the total supply based on a collateral amount.
+* ERC20Custodian: Extension of ERC20 that implements an access-control agnostic approach to define a custodian that can freeze user’s transfers and approvals.
+* ERC4626Fees: ERC4626 vault with fees on entry (deposit/mint) or exit (withdraw/redeem).
+
+## General
+
+OnTokenTransferAdapter
+
+## ERC20
+
+ERC20Allowlist
+
+ERC20Blocklist
+
+ERC20Collateral
+
+ERC20Custodian
+
+ERC4626Fees
diff --git a/contracts/utils/README.adoc b/contracts/utils/README.adoc
deleted file mode 100644
index 820a05b1..00000000
--- a/contracts/utils/README.adoc
+++ /dev/null
@@ -1,19 +0,0 @@
-= Utilities
-
-[.readme-notice]
-NOTE: This document is better viewed at https://docs.openzeppelin.com/community-contracts/utils
-
-Miscellaneous contracts and libraries containing utility functions you can use to improve security, work with new data types, or safely use low-level primitives.
-
- * {EnumerableSetExtended} and {EnumerableMapExtended}: Extensions of the `EnumerableSet` and `EnumerableMap` libraries with more types, including non-value types.
- * {Masks}: Library to handle `bytes32` masks.
-
-== Structs
-
-{{EnumerableSetExtended}}
-
-{{EnumerableMapExtended}}
-
-== Libraries
-
-{{Masks}}
diff --git a/contracts/utils/README.mdx b/contracts/utils/README.mdx
new file mode 100644
index 00000000..d81c0d8b
--- /dev/null
+++ b/contracts/utils/README.mdx
@@ -0,0 +1,22 @@
+---
+title: Utilities
+---
+
+
+This document is better viewed at https://docs.openzeppelin.com/community-contracts/utils
+
+
+Miscellaneous contracts and libraries containing utility functions you can use to improve security, work with new data types, or safely use low-level primitives.
+
+* EnumerableSetExtended and EnumerableMapExtended: Extensions of the `EnumerableSet` and `EnumerableMap` libraries with more types, including non-value types.
+* Masks: Library to handle `bytes32` masks.
+
+## Structs
+
+EnumerableSetExtended
+
+EnumerableMapExtended
+
+## Libraries
+
+Masks
diff --git a/contracts/utils/cryptography/DKIMRegistry.sol b/contracts/utils/cryptography/DKIMRegistry.sol
index 15204364..ca92ebc9 100644
--- a/contracts/utils/cryptography/DKIMRegistry.sol
+++ b/contracts/utils/cryptography/DKIMRegistry.sol
@@ -4,7 +4,7 @@ pragma solidity ^0.8.26;
import {IDKIMRegistry} from "../../interfaces/IERC7969.sol";
/**
- * @dev Implementation of the https://eips.ethereum.org/EIPS/eip-7969[ERC-7969] interface for registering
+ * @dev Implementation of the [ERC-7969](https://eips.ethereum.org/EIPS/eip-7969) interface for registering
* and validating DomainKeys Identified Mail (DKIM) public key hashes onchain.
*
* This contract provides a standard way to register and validate DKIM public key hashes, enabling
@@ -48,8 +48,10 @@ abstract contract DKIMRegistry is IDKIMRegistry {
*
* Emits a {KeyHashRegistered} event.
*
- * NOTE: This function does not validate that keyHash is non-zero. Consider adding
+ *
+ * This function does not validate that keyHash is non-zero. Consider adding
* validation in derived contracts if needed.
+ *
*/
function _setKeyHash(bytes32 domainHash, bytes32 keyHash) internal {
_keyHashes[domainHash][keyHash] = true;
@@ -62,8 +64,10 @@ abstract contract DKIMRegistry is IDKIMRegistry {
*
* Emits a {KeyHashRegistered} event for each key hash.
*
- * NOTE: This function does not validate that the keyHashes array is non-empty.
+ *
+ * This function does not validate that the keyHashes array is non-empty.
* Consider adding validation in derived contracts if needed.
+ *
*/
function _setKeyHashes(bytes32 domainHash, bytes32[] memory keyHashes) internal {
for (uint256 i = 0; i < keyHashes.length; ++i) {
diff --git a/contracts/utils/cryptography/README.adoc b/contracts/utils/cryptography/README.adoc
deleted file mode 100644
index f68492a2..00000000
--- a/contracts/utils/cryptography/README.adoc
+++ /dev/null
@@ -1,25 +0,0 @@
-= Cryptography
-
-[.readme-notice]
-NOTE: This document is better viewed at https://docs.openzeppelin.com/community-contracts/utils/cryptography
-
-A collection of contracts and libraries that implement various signature validation schemes and cryptographic primitives. These utilities enable secure authentication, multisignature operations, and advanced cryptographic operations in smart contracts.
-
- * {ZKEmailUtils}: Library for ZKEmail signature validation utilities, enabling email-based authentication through zero-knowledge proofs.
- * {DKIMRegistry}: Implementation of https://eips.ethereum.org/EIPS/eip-7969[ERC-7969] to enable onchain verification of DomainKeys Identified Mail (DKIM) signatures.
- * {SignerZKEmail}: Implementation of an https://docs.openzeppelin.com/contracts/5.x/api/utils/cryptography#AbstractSigner[AbstractSigner] that enables email-based authentication through zero-knowledge proofs.
- * {ERC7913ZKEmailVerifier}: Ready to use ERC-7913 signature verifiers for ZKEmail.
-
-== Utils
-
-{{ZKEmailUtils}}
-
-{{DKIMRegistry}}
-
-== Abstract Signers
-
-{{SignerZKEmail}}
-
-== Verifiers
-
-{{ERC7913ZKEmailVerifier}}
diff --git a/contracts/utils/cryptography/README.mdx b/contracts/utils/cryptography/README.mdx
new file mode 100644
index 00000000..2c58ef08
--- /dev/null
+++ b/contracts/utils/cryptography/README.mdx
@@ -0,0 +1,32 @@
+---
+title: Cryptography
+---
+
+
+This document is better viewed at https://docs.openzeppelin.com/community-contracts/utils/cryptography
+
+
+A collection of contracts and libraries that implement various signature validation schemes and cryptographic primitives. These utilities enable secure authentication, multisignature operations, and advanced cryptographic operations in smart contracts.
+
+* ZKEmailUtils: Library for ZKEmail signature validation utilities, enabling email-based authentication through zero-knowledge proofs.
+* DKIMRegistry: Implementation of [ERC-7969](https://eips.ethereum.org/EIPS/eip-7969) to enable onchain verification of DomainKeys Identified Mail (DKIM) signatures.
+* SignerZKEmail: Implementation of an [AbstractSigner](https://docs.openzeppelin.com/contracts/5.x/api/utils/cryptography#AbstractSigner) that enables email-based authentication through zero-knowledge proofs.
+* ERC7913ZKEmailVerifier, ERC7913WebAuthnVerifier: Ready to use ERC-7913 signature verifiers for ZKEmail and WebAuthn.
+
+## Utils
+
+ZKEmailUtils
+
+WebAuthn
+
+DKIMRegistry
+
+## Abstract Signers
+
+SignerZKEmail
+
+## Verifiers
+
+ERC7913ZKEmailVerifier
+
+ERC7913WebAuthnVerifier
diff --git a/contracts/utils/cryptography/ZKEmailUtils.sol b/contracts/utils/cryptography/ZKEmailUtils.sol
index 0440ea5d..28587b3c 100644
--- a/contracts/utils/cryptography/ZKEmailUtils.sol
+++ b/contracts/utils/cryptography/ZKEmailUtils.sol
@@ -10,7 +10,7 @@ import {EmailProof} from "@zk-email/email-tx-builder/src/interfaces/IEmailTypes.
import {CommandUtils} from "@zk-email/email-tx-builder/src/libraries/CommandUtils.sol";
/**
- * @dev Library for https://docs.zk.email[ZKEmail] Groth16 proof validation utilities.
+ * @dev Library for [ZKEmail](https://docs.zk.email) Groth16 proof validation utilities.
*
* ZKEmail is a protocol that enables email-based authentication and authorization for smart contracts
* using zero-knowledge proofs. It allows users to prove ownership of an email address without revealing
@@ -18,11 +18,11 @@ import {CommandUtils} from "@zk-email/email-tx-builder/src/libraries/CommandUtil
*
* The validation process involves several key components:
*
- * * A https://docs.zk.email/architecture/dkim-verification[DKIMRegistry] (DomainKeys Identified Mail) verification
+ * * A [DKIMRegistry](https://docs.zk.email/architecture/dkim-verification) (DomainKeys Identified Mail) verification
* mechanism to ensure the email was sent from a valid domain. Defined by an `IDKIMRegistry` interface.
- * * A https://docs.zk.email/email-tx-builder/architecture/command-templates[command template] validation
+ * * A [command template](https://docs.zk.email/email-tx-builder/architecture/command-templates) validation
* mechanism to ensure the email command matches the expected format and parameters.
- * * A https://docs.zk.email/architecture/zk-proofs#how-zk-email-uses-zero-knowledge-proofs[zero-knowledge proof] verification
+ * * A [zero-knowledge proof](https://docs.zk.email/architecture/zk-proofs#how-zk-email-uses-zero-knowledge-proofs) verification
* mechanism to ensure the email was actually sent and received without revealing its contents. Defined by an `IGroth16Verifier` interface.
*/
library ZKEmailUtils {
@@ -80,7 +80,9 @@ library ZKEmailUtils {
* Returns {EmailProofError.NoError} if all validations pass, or a specific {EmailProofError} indicating
* which validation check failed.
*
- * NOTE: Attempts to validate the command for all possible string {Case} values.
+ *
+ * Attempts to validate the command for all possible string {Case} values.
+ *
*/
function isValidZKEmail(
EmailProof memory emailProof,
@@ -131,8 +133,10 @@ library ZKEmailUtils {
* @dev Verifies that calldata bytes (`input`) represents a valid `EmailProof` object. If encoding is valid,
* returns true and the calldata view at the object. Otherwise, returns false and an invalid calldata object.
*
- * NOTE: The returned `emailProof` object should not be accessed if `success` is false. Trying to access the data may
+ *
+ * The returned `emailProof` object should not be accessed if `success` is false. Trying to access the data may
* cause revert/panic.
+ *
*/
function tryDecodeEmailProof(
bytes calldata input
diff --git a/contracts/utils/cryptography/signers/SignerZKEmail.sol b/contracts/utils/cryptography/signers/SignerZKEmail.sol
index 7c196d00..8df81087 100644
--- a/contracts/utils/cryptography/signers/SignerZKEmail.sol
+++ b/contracts/utils/cryptography/signers/SignerZKEmail.sol
@@ -9,7 +9,7 @@ import {AbstractSigner} from "@openzeppelin/contracts/utils/cryptography/signers
import {ZKEmailUtils} from "../ZKEmailUtils.sol";
/**
- * @dev Implementation of {AbstractSigner} using https://docs.zk.email[ZKEmail] signatures.
+ * @dev Implementation of {AbstractSigner} using [ZKEmail](https://docs.zk.email) signatures.
*
* ZKEmail enables secure authentication and authorization through email messages, leveraging
* DKIM signatures from a {DKIMRegistry} and zero-knowledge proofs enabled by a {verifier}
@@ -41,9 +41,11 @@ import {ZKEmailUtils} from "../ZKEmailUtils.sol";
* }
* ```
*
- * IMPORTANT: Failing to call {_setAccountSalt}, {_setDKIMRegistry}, and {_setVerifier}
+ *
+ * Failing to call {_setAccountSalt}, {_setDKIMRegistry}, and {_setVerifier}
* either during construction (if used standalone) or during initialization (if used as a clone) may
* leave the signer either front-runnable or unusable.
+ *
*/
abstract contract SignerZKEmail is AbstractSigner {
using ZKEmailUtils for EmailProof;
@@ -58,7 +60,7 @@ abstract contract SignerZKEmail is AbstractSigner {
/**
* @dev Unique identifier for owner of this contract defined as a hash of an email address and an account code.
*
- * An account code is a random integer in a finite scalar field of https://neuromancer.sk/std/bn/bn254[BN254] curve.
+ * An account code is a random integer in a finite scalar field of [BN254](https://neuromancer.sk/std/bn/bn254) curve.
* It is a private randomness to derive a CREATE2 salt of the user's Ethereum address
* from the email address, i.e., userEtherAddr := CREATE2(hash(userEmailAddr, accountCode)).
*
@@ -76,7 +78,7 @@ abstract contract SignerZKEmail is AbstractSigner {
}
/// @dev An instance of the DKIM registry contract.
- /// See https://docs.zk.email/architecture/dkim-verification[DKIM Verification].
+ /// See [DKIM Verification](https://docs.zk.email/architecture/dkim-verification).
// solhint-disable-next-line func-name-mixedcase
function DKIMRegistry() public view virtual returns (IDKIMRegistry) {
return _registry;
@@ -84,7 +86,7 @@ abstract contract SignerZKEmail is AbstractSigner {
/**
* @dev An instance of the Groth16Verifier contract.
- * See https://docs.zk.email/architecture/zk-proofs#how-zk-email-uses-zero-knowledge-proofs[ZK Proofs].
+ * See [ZK Proofs](https://docs.zk.email/architecture/zk-proofs#how-zk-email-uses-zero-knowledge-proofs).
*/
function verifier() public view virtual returns (IGroth16Verifier) {
return _groth16Verifier;
diff --git a/contracts/utils/structs/EnumerableMapExtended.sol b/contracts/utils/structs/EnumerableMapExtended.sol
index 4c2c7a3c..79d4d309 100644
--- a/contracts/utils/structs/EnumerableMapExtended.sol
+++ b/contracts/utils/structs/EnumerableMapExtended.sol
@@ -8,7 +8,7 @@ import {EnumerableSetExtended} from "./EnumerableSetExtended.sol";
/**
* @dev Library for managing an enumerable variant of Solidity's
- * https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`]
+ * [`mapping`](https://solidity.readthedocs.io/en/latest/types.html#mapping-types)
* type for non-value types as keys.
*
* Maps have the following properties:
@@ -33,17 +33,18 @@ import {EnumerableSetExtended} from "./EnumerableSetExtended.sol";
* - `bytes -> uint256` (`BytesToUintMap`)
* - `string -> string` (`StringToStringMap`)
*
- * [WARNING]
- * ====
+ *
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
- * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
+ * See [ethereum/solidity#11843](https://github.com/ethereum/solidity/pull/11843) for more info.
*
* In order to clean an EnumerableMap, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableMap.
- * ====
+ *
*
- * NOTE: Extensions of openzeppelin/contracts/utils/struct/EnumerableMap.sol.
+ *
+ * Extensions of openzeppelin/contracts/utils/struct/EnumerableMap.sol.
+ *
*/
library EnumerableMapExtended {
using EnumerableSet for *;
@@ -85,8 +86,10 @@ library EnumerableMapExtended {
/**
* @dev Removes all the entries from a map. O(n).
*
- * WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
+ *
+ * Developers should keep in mind that this function has an unbounded cost and using it may render the
* function uncallable if the map grows to the point where clearing it consumes too much gas to fit in a block.
+ *
*/
function clear(BytesToUintMap storage map) internal {
uint256 len = length(map);
@@ -152,10 +155,12 @@ library EnumerableMapExtended {
/**
* @dev Returns an array containing all the keys
*
- * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
+ *
+ * This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
+ *
*/
function keys(BytesToUintMap storage map) internal view returns (bytes[] memory) {
return map._keys.values();
@@ -164,10 +169,12 @@ library EnumerableMapExtended {
/**
* @dev Returns an array containing a slice of the keys
*
- * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
+ *
+ * This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
+ *
*/
function keys(BytesToUintMap storage map, uint256 start, uint256 end) internal view returns (bytes[] memory) {
return map._keys.values(start, end);
@@ -209,8 +216,10 @@ library EnumerableMapExtended {
/**
* @dev Removes all the entries from a map. O(n).
*
- * WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
+ *
+ * Developers should keep in mind that this function has an unbounded cost and using it may render the
* function uncallable if the map grows to the point where clearing it consumes too much gas to fit in a block.
+ *
*/
function clear(StringToStringMap storage map) internal {
uint256 len = length(map);
@@ -282,10 +291,12 @@ library EnumerableMapExtended {
/**
* @dev Returns an array containing all the keys
*
- * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
+ *
+ * This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
+ *
*/
function keys(StringToStringMap storage map) internal view returns (string[] memory) {
return map._keys.values();
@@ -294,10 +305,12 @@ library EnumerableMapExtended {
/**
* @dev Returns an array containing a slice of the keys
*
- * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
+ *
+ * This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
+ *
*/
function keys(StringToStringMap storage map, uint256 start, uint256 end) internal view returns (string[] memory) {
return map._keys.values(start, end);
diff --git a/contracts/utils/structs/EnumerableSetExtended.sol b/contracts/utils/structs/EnumerableSetExtended.sol
index 58a7d92b..83221262 100644
--- a/contracts/utils/structs/EnumerableSetExtended.sol
+++ b/contracts/utils/structs/EnumerableSetExtended.sol
@@ -9,7 +9,7 @@ import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
/**
* @dev Library for managing
- * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of non-value
+ * [sets](https://en.wikipedia.org/wiki/Set_(abstract_data_type)) of non-value
* types.
*
* Sets have the following properties:
@@ -32,17 +32,18 @@ import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
* Sets of type `string` (`StringSet`), `bytes` (`BytesSet`) and
* `bytes32[2]` (`Bytes32x2Set`) are supported.
*
- * [WARNING]
- * ====
+ *
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
- * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
+ * See [ethereum/solidity#11843](https://github.com/ethereum/solidity/pull/11843) for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
- * ====
+ *
*
- * NOTE: This is an extension of openzeppelin/contracts/utils/struct/EnumerableSet.sol.
+ *
+ * This is an extension of openzeppelin/contracts/utils/struct/EnumerableSet.sol.
+ *
*/
library EnumerableSetExtended {
struct Bytes32x2Set {
@@ -115,8 +116,10 @@ library EnumerableSetExtended {
/**
* @dev Removes all the values from a set. O(n).
*
- * WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
+ *
+ * Developers should keep in mind that this function has an unbounded cost and using it may render the
* function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
+ *
*/
function clear(Bytes32x2Set storage self) internal {
bytes32[2][] storage v = self._values;
@@ -161,10 +164,12 @@ library EnumerableSetExtended {
/**
* @dev Return the entire set in an array
*
- * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
+ *
+ * This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
+ *
*/
function values(Bytes32x2Set storage self) internal view returns (bytes32[2][] memory) {
return self._values;
@@ -173,10 +178,12 @@ library EnumerableSetExtended {
/**
* @dev Return a slice of the set in an array
*
- * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
+ *
+ * This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
+ *
*/
function values(Bytes32x2Set storage set, uint256 start, uint256 end) internal view returns (bytes32[2][] memory) {
unchecked {
diff --git a/docs/antora.yml b/docs/antora.yml
deleted file mode 100644
index d31c3d14..00000000
--- a/docs/antora.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-name: community-contracts
-title: Community Contracts
-version: 0.0.1
-prerelease: true
-nav:
- - modules/ROOT/nav.adoc
- - modules/api/nav.adoc
diff --git a/docs/config.js b/docs/config.js
index f0af6639..83329eef 100644
--- a/docs/config.js
+++ b/docs/config.js
@@ -6,14 +6,13 @@ module.exports = {
outputDir: 'docs/modules/api/pages',
templates: 'docs/templates',
exclude: ['mocks'],
- pageExtension: '.adoc',
+ pageExtension: '.mdx',
pages: (_, file, config) => {
- // For each contract file, find the closest README.adoc and return its location as the output page path.
const sourcesDir = path.resolve(config.root, config.sourcesDir);
let dir = path.resolve(config.root, file.absolutePath);
while (dir.startsWith(sourcesDir)) {
dir = path.dirname(dir);
- if (fs.existsSync(path.join(dir, 'README.adoc'))) {
+ if (fs.existsSync(path.join(dir, 'README.mdx'))) {
return path.relative(sourcesDir, dir) + config.pageExtension;
}
}
diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc
deleted file mode 100644
index 77f2b393..00000000
--- a/docs/modules/ROOT/nav.adoc
+++ /dev/null
@@ -1,7 +0,0 @@
-* xref:index.adoc[Overview]
-* xref:contracts::account-abstraction.adoc[Account Abstraction]
-** xref:contracts::accounts.adoc[Accounts]
-*** xref:account-modules.adoc[Modules]
-** xref:paymasters.adoc[Paymasters]
-* xref:crosschain.adoc[Crosschain]
-* xref:utilities.adoc[Utilities]
diff --git a/docs/modules/ROOT/pages/account-modules.adoc b/docs/modules/ROOT/pages/account-modules.adoc
deleted file mode 100644
index 15cc7925..00000000
--- a/docs/modules/ROOT/pages/account-modules.adoc
+++ /dev/null
@@ -1,129 +0,0 @@
-= Account Modules
-
-Smart accounts built with https://eips.ethereum.org/EIPS/eip-7579[ERC-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 https://erc7579.com/modules[compatible modules].
-
-== ERC-7579
-
-ERC-7579 defines a standardized interface for modular smart accounts. This standard enables accounts to install, uninstall, and interact with modules that extend their capabilities in a composable manner with different account implementations.
-
-=== Accounts
-
-OpenZeppelin offers an implementation of an xref:api:account.adoc#AccountERC7579[`AccountERC7579`] contract that allows installing modules compliant with this standard. There's also an xref:api:account.adoc#AccountERC7579Hooked[`AccountERC7579Hooked`] variant that supports installation of hooks. Like xref:accounts.adoc#handling_initialization[most accounts], an instance should define an initializer function where the first module that controls the account will be set:
-
-[source,solidity]
-----
-include::api:example$account/MyAccountERC7579.sol[]
-----
-
-NOTE: For simplicity, the xref:api:account.adoc#AccountERC7579Hooked[`AccountERC7579Hooked`] only supports a single hook. A common workaround is to install a https://github.com/rhinestonewtf/core-modules/blob/7afffccb44d73dbaca2481e7b92bce0621ea6449/src/HookMultiPlexer/HookMultiPlexer.sol[single hook with a multiplexer pattern] to extend the functionality to multiple hooks.
-
-=== Modules
-
-Functionality is added to accounts through encapsulated functionality deployed as smart contracts called _modules_. The standard defines four primary module types:
-
-* *Validator modules (type 1)*: Handle signature verification and user operation validation
-* *Executor modules (type 2)*: Execute operations on behalf of the account
-* *Fallback modules (type 3)*: Handle fallback calls for specific function selectors
-* *Hook modules (type 4)*: Execute logic before and after operations
-
-Modules can implement multiple types simultaneously, which means you could combine an executor module with hooks to enforce behaviors on an account, such as maintaining ERC-20 approvals or preventing the removal of certain permissions.
-
-See https://erc7579.com/modules[popular module implementations].
-
-==== Building Custom Modules
-
-The library provides _standard composable modules_ as building blocks with an internal API for developers. By combining these components, you can create a rich set of variants without including unnecessary features.
-
-A good starting point is the xref:api:account.adoc#ERC7579Executor[`ERC7579Executor`] or xref:api:account.adoc#ERC7579Validator[`ERC7579Validator`], which include an opinionated base layer easily combined with other abstract modules. Hooks and fallback handlers are more straightforward to implement directly from interfaces:
-
-[source,solidity]
-----
-include::api:example$account/modules/MyERC7579Modules.sol[]
-----
-
-TIP: Explore these abstract ERC-7579 modules in the xref:api:account.adoc#modules[API Reference].
-
-==== Execution Modes
-
-ERC-7579 supports various execution modes, which are encoded as a `bytes32` value. The https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/account/utils/draft-ERC7579Utils.sol[`ERC7579Utils`] library provides utility functions to work with these modes:
-
-[source,solidity]
-----
-// Parts of an execution mode
-type Mode is bytes32;
-type CallType is bytes1;
-type ExecType is bytes1;
-type ModeSelector is bytes4;
-type ModePayload is bytes22;
-----
-
-===== Call Types
-
-Call types determine the kind of execution:
-
-[%header,cols="1,1,3"]
-|===
-|Type |Value |Description
-|`CALLTYPE_SINGLE` |`0x00` |A single `call` execution
-|`CALLTYPE_BATCH` |`0x01` |A batch of `call` executions
-|`CALLTYPE_DELEGATECALL` |`0xFF` |A `delegatecall` execution
-|===
-
-===== Execution Types
-
-Execution types determine how failures are handled:
-
-[%header,cols="1,1,3"]
-|===
-|Type |Value |Description
-|`EXECTYPE_DEFAULT` |`0x00` |Reverts on failure
-|`EXECTYPE_TRY` |`0x01` |Does not revert on failure, emits an event instead
-|===
-
-==== Execution Data Format
-
-The execution data format varies depending on the call type:
-
-* For single calls: `abi.encodePacked(target, value, callData)`
-* For batched calls: `abi.encode(Execution[])` where `Execution` is a struct containing `target`, `value`, and `callData`
-* For delegate calls: `abi.encodePacked(target, callData)`
-
-== Examples
-
-=== Social Recovery
-
-Social recovery allows an account to be recovered when access is lost by relying on trusted parties ("guardians") who verify the user's identity and help restore access.
-
-Social recovery is not a single solution but a design space with multiple configuration options:
-
-* Delay configuration
-* Expiration settings
-* Different guardian types
-* Cancellation windows
-* Confirmation requirements
-
-To support _different guardian types_, we can leverage ERC-7913 as discussed in the xref:multisig.adoc#beyond_standard_signature_verification[multisig] section. For ERC-7579 modules, this is implemented through the xref:api:account.adoc#ERC7579Multisig[`ERC7579Multisig`] validator.
-
-Combined with an xref:api:account.adoc#ERC7579Executor[`ERC7579Executor`], it provides a basic foundation that can be extended with more sophisticated features:
-
-[source,solidity]
-----
-include::api:example$account/modules/MyERC7579SocialRecovery.sol[]
-----
-
-For enhanced security, you can extend this foundation with scheduling, delays, and cancellations using xref:api:account.adoc#ERC7579DelayedExecutor[`ERC7579DelayedExecutor`]. This allows guardians to schedule recovery operations with a time delay, providing a security window to detect and cancel suspicious recovery attempts before they execute:
-
-[source,solidity]
-----
-include::api:example$account/modules/MyERC7579DelayedSocialRecovery.sol[]
-----
-
-NOTE: The delayed executor's signature validation doesn't require a nonce since operations are uniquely identified by their xref:api:account.adoc#ERC7579DelayedExecutor-hashOperation-address-bytes32-bytes32-bytes-[operation id] and cannot be scheduled twice.
-
-These implementations demonstrate how to build progressively more secure social recovery mechanisms, from basic multi-signature recovery to time-delayed recovery with cancellation capabilities.
-
-For additional functionality, developers can use:
-
-* xref:api:account.adoc#ERC7579MultisigWeighted[`ERC7579MultisigWeighted`] to assign different weights to signers
-* xref:api:account.adoc#ERC7579MultisigConfirmation[`ERC7579MultisigConfirmation`] to implement a confirmation system that verifies signatures when adding signers
-* xref:api:account.adoc#ERC7579MultisigStorage[`ERC7579MultisigStorage`] to allow guardians to presign recovery operations for more flexible coordination
diff --git a/docs/modules/ROOT/pages/crosschain.adoc b/docs/modules/ROOT/pages/crosschain.adoc
deleted file mode 100644
index 1e7b5d6d..00000000
--- a/docs/modules/ROOT/pages/crosschain.adoc
+++ /dev/null
@@ -1,176 +0,0 @@
-= 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.
-
-The variety of these bridges is outlined in https://x.com/norswap[@norswap]'s https://github.com/0xFableOrg/xchain/blob/master/README.md[Cross-Chain Interoperability Report] that proposes https://github.com/0xFableOrg/xchain/blob/master/README.md#bridge-taxonomy[a taxonomy of 7 bridge categories]. This diversity makes it difficult for developers to design cross-chain applications given the lack of portability.
-
-This guide will teach you how to follow https://eips.ethereum.org/EIPS/eip-7786[ERC-7786] to establish messaging gateways across chains regardless of the underlying bridge. Developers can implement gateway contracts that process cross-chain messages and connect any crosschain protocol they want (or implement themselves).
-
-== ERC-7786 Gateway
-
-To address the lack of composability in a simple and unopinionated way, ERC-7786 proposes a standard for implementing gateways that relay messages to other chains. This generalized approach is expressive enough to enable new types of applications and can be adapted to any bridge taxonomy or specific bridge interface with standardized attributes.
-
-=== Message passing overview
-
-The ERC defines a source and a destination gateway. Both are contracts that implement a protocol to send a message and process its reception respectively. These two processes are identified explicitly by the ERC-7786 specification since they define the minimal requirements for both gateways.
-
-* On the **source chain**, the contract implements a standard xref:api:crosschain.adoc#AxelarGatewaySource-sendMessage-bytes-bytes-bytes---[`sendMessage`] function and emits a xref:api:crosschain.adoc#AxelarGatewaySource-MessageSent-bytes32-string-string-bytes-bytes---[`MessageSent`] event to signal that the message should be relayed by the underlying protocol.
-
-* On the **destination chain**, the gateway receives the message and passes it to the receiver contract by calling the xref:api:crosschain.adoc#ERC7786Receiver-receiveMessage-bytes32-bytes-bytes-[`receiveMessage`] function.
-
-Smart contract developers only need to worry about implementing the xref:api:crosschain.adoc#IERC7786GatewaySource[IERC7786GatewaySource] interface to send a message on the source chain and the xref:api:crosschain.adoc#IERC7786GatewaySource[IERC7786GatewaySource] and xref:api:crosschain.adoc#IERC7786Receiver[IERC7786Receiver] interface to receive such message on the destination chain.
-
-=== Getting started with Axelar Network
-
-To start sending cross-chain messages, developers can get started with a duplex gateway powered by Axelar Network. This will allow a contract to send or receive cross-chain messages leveraging automated execution by Axelar relayers on the destination chain.
-
-[source,solidity]
-----
-include::api:example$crosschain/MyCustomAxelarGatewayDuplex.sol[]
-----
-
-For more details of how the duplex gateway works, see xref:crosschain.adoc#axelar_network[how to send and receive messages with the Axelar Network] below
-
-NOTE: Developers can register supported chains and destination gateways using the xref:api:crosschain.adoc#AxelarGatewayBase-registerChainEquivalence-string-string-[`registerChainEquivalence`] and xref:api:crosschain.adoc#AxelarGatewayBase-registerRemoteGateway-string-string-[`registerRemoteGateway`] functions
-
-== Cross-chain communication
-
-=== Sending a message
-
-The interface for a source gateway is general enough that it allows wrapping a custom protocol to authenticate messages. Depending on the use case, developers can implement any offchain mechanism to read the standard xref:api:crosschain.adoc#IERC7786GatewaySource-MessageSent-bytes32-string-string-bytes-bytes---[`MessageSent`] event and deliver it to the receiver on the destination chain.
-
-[source,solidity]
-----
-include::api:example$crosschain/MyERC7786GatewaySource.sol[]
-----
-
-NOTE: The standard represents chains using https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-2.md[CAIP-2] identifiers and accounts using https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-2.md[CAIP-10] identifiers for increased interoperability with non-EVM chains. Consider using the Strings library in the contracts library to process these identifiers.
-
-=== Receiving a message
-
-To successfully process a message on the destination chain, a destination gateway is required. Although ERC-7786 doesn't define a standard interface for the destination gateway, it requires that it calls the `receiveMessage` upon message reception.
-
-Every cross-chain message protocol already offers a way to receive the message either through a canonical bridge or an intermediate contract. Developers can easily wrap the receiving contract into a gateway that calls the `receiveMessage` function as mandated by the ERC.
-
-To receive a message on a custom smart contract, OpenZeppelin Community Contracts provide an xref:api:crosschain.adoc#ERC7786Receiver[ERC7786Receiver] implementation for developers to inherit. This way your contracts can receive a cross-chain message relayed through a known destination gateway gateway.
-
-[source,solidity]
-----
-include::api:example$crosschain/MyERC7786ReceiverContract.sol[]
-----
-
-The standard receiving interface abstracts away the underlying protocol. This way, it is possible for a contract to send a message through an ERC-7786 compliant gateway (or through an adapter) and get it received on the destination chain without worrying about the protocol implementation details.
-
-=== Axelar Network
-
-Aside from the xref:api:crosschain.adoc#AxelarGatewayDuplex[AxelarGatewayDuplex], the library offers an implementation of the xref:api:crosschain.adoc#IERC7786GatewaySource[IERC7786GatewaySource] interface called xref:api:crosschain.adoc#AxelarGatewaySource[AxelarGatewaySource] that works as an adapter for sending messages in compliance with ERC-7786
-
-The implementation takes a local gateway address that MUST correspond to https://axelarscan.io/resources/chains?type=evm[Axelar's native gateways] and has mechanisms to:
-
-* Keep track of equivalences between Axelar chain names and CAIP-2 identifiers
-* Record a destination gateway per network using their CAIP-2 identifier
-
-The xref:api:crosschain.adoc#AxelarGatewaySource[AxelarGatewaySource] implementation can be used out of the box
-
-[source,solidity]
-----
-include::api:example$crosschain/MyCustomAxelarGatewaySource.sol[]
-----
-
-For a destination gateway, the library provides an adapter of the `AxelarExecutable` interface to receive messages and relay them to an xref:api:crosschain.adoc#IERC7786Receiver[IERC7786Receiver].
-
-[source,solidity]
-----
-include::api:example$crosschain/MyCustomAxelarGatewayDestination.sol[]
-----
-
-=== Open Bridge
-
-The xref:api:crosschain.adoc#ERC7786OpenBridge[ERC7786OpenBridge] is a special gateway that implements both xref:api:crosschain.adoc#IERC7786GatewaySource[IERC7786GatewaySource] and xref:api:crosschain.adoc#IERC7786Receiver[IERC7786Receiver] interfaces. It provides a way to send messages across multiple bridges simultaneously and ensures message delivery through a threshold-based confirmation system.
-
-The bridge maintains a list of known gateways and a confirmation threshold. When sending a message, it broadcasts to all registered gateways, and when receiving, it requires a minimum number of confirmations before executing the message. This approach increases reliability by ensuring messages are properly delivered and validated across multiple bridges.
-
-When sending a message, the bridge tracks the message IDs from each gateway to maintain a record of the message's journey across different bridges:
-
-```solidity
-function sendMessage(
- string calldata destinationChain,
- string memory receiver,
- bytes memory payload,
- bytes[] memory attributes
-) public payable virtual whenNotPaused returns (bytes32 outboxId) {
-
- // ... Initialize variables and prepare payload ...
-
- // Post on all gateways
- Outbox[] memory outbox = new Outbox[](_gateways.length());
- bool needsId = false;
- for (uint256 i = 0; i < outbox.length; ++i) {
- address gateway = _gateways.at(i);
- // send message
- bytes32 id = IERC7786GatewaySource(gateway).sendMessage(
- destinationChain,
- bridge,
- wrappedPayload,
- attributes
- );
- // if ID, track it
- if (id != bytes32(0)) {
- outbox[i] = Outbox(gateway, id);
- needsId = true;
- }
- }
-
- // ... Handle message tracking and return value ...
-}
-```
-
-On the receiving end, the bridge implements a threshold-based confirmation system. Messages are only executed after receiving enough confirmations from the gateways, ensuring message validity and preventing double execution. The xref:api:crosschain.adoc#ERC7786OpenBridge-receiveMessage-string-string-string-bytes-bytes---[`receiveMessage`] function handles this process:
-
-```solidity
-function receiveMessage(
- string calldata /*messageId*/, // gateway specific, empty or unique
- string calldata sourceChain, // CAIP-2 chain identifier
- string calldata sender, // CAIP-10 account address (does not include the chain identifier)
- bytes calldata payload,
- bytes[] calldata attributes
-) public payable virtual whenNotPaused returns (bytes4) {
-
- // ... Validate message format and extract message ID ...
-
- // If call is first from a trusted gateway
- if (_gateways.contains(msg.sender) && !tracker.receivedBy[msg.sender]) {
- // Count number of time received
- tracker.receivedBy[msg.sender] = true;
- ++tracker.countReceived;
- emit Received(id, msg.sender);
-
- // if already executed, leave gracefully
- if (tracker.executed) return IERC7786Receiver.receiveMessage.selector;
- } else if (tracker.executed) {
- revert ERC7786OpenBridgeAlreadyExecuted();
- }
-
- // ... Validate sender and prepare payload for execution ...
-
- // If ready to execute, and not yet executed
- if (tracker.countReceived >= getThreshold()) {
- // prevent re-entry
- tracker.executed = true;
-
- // ... Prepare execution context and validate state ...
- bytes memory call = abi.encodeCall(
- IERC7786Receiver.receiveMessage,
- (uint256(id).toHexString(32), sourceChain, originalSender, unwrappedPayload, attributes)
- );
-
- (bool success, bytes memory returndata) = receiver.parseAddress().call(call);
-
- // ... Handle the result ...
- }
-
- return IERC7786Receiver.receiveMessage.selector;
-}
-```
-
-The bridge is designed to be configurable. As an `Ownable` contract, it allows the owner to manage the list of trusted gateways and adjust the confirmation threshold. The `_gateways` list and threshold are initially set during contract deployment using the xref:api:crosschain.adoc#ERC7786OpenBridge-_addGateway-address-[`++_addGateway++`] and xref:api:crosschain.adoc#ERC7786OpenBridge-_setThreshold-uint8-[`++_setThreshold++`] functions. The owner can update these settings as needed to adapt to changing requirements or add new gateways.
diff --git a/docs/modules/ROOT/pages/index.adoc b/docs/modules/ROOT/pages/index.adoc
deleted file mode 100644
index 5e2243f3..00000000
--- a/docs/modules/ROOT/pages/index.adoc
+++ /dev/null
@@ -1,47 +0,0 @@
-= Community Contracts
-
-*A community-driven extension of our https://docs.openzeppelin.com/contracts[Solidity library]*: the gold-standard of smart contract development. This library includes:
-
-* Extensions and modules compatible with contracts in the original package
-* Alternative implementation of interfaces defined in the original package
-* Contracts with third-party integrations
-* Contracts built by community members, that align with OpenZeppelin offerings
-* General prototypes and experiments
-
-Code is provided by the OpenZeppelin Contracts team, as well as by community contributors, for other developers to review, discuss, iterate on, and potentially use.
-
-== Overview
-
-[[install]]
-=== Installation
-
-Given this extension is intended for more experimental use cases and therefore the development process is more flexible. For such reason, the library can only be installed with Foundry using gitmodules.
-
-==== Foundry (git)
-
-```console
-$ forge install OpenZeppelin/openzeppelin-community-contracts
-```
-
-NOTE: Make sure to add `@openzeppelin/community-contracts/=lib/openzeppelin-community-contracts/contracts/` in `remappings.txt.`
-
-[[usage]]
-=== Usage
-
-Once installed, you can use the contracts in the library by importing them:
-
-[source,solidity]
-----
-include::api:example$MyStablecoinAllowlist.sol[]
-----
-
-To keep your system secure, you should **always** use the installed code as-is, and neither copy-paste it from online sources, nor modify it yourself. The library is designed so that only the contracts and functions you use are deployed, so you don't need to worry about it needlessly increasing gas costs.
-
-[[security]]
-== Security
-
-Contracts in the community library are provided as is, with no particular guarantees. Given changes in this repository are more frequent, the code is not formally audited and not covered by the https://www.immunefi.com/bounty/openzeppelin[bug bounty program on Immunefi].
-
-Similarly, the code has no backward compatibility guarantees.
-
-We kindly ask to report any issue directly to our security mailto:security@openzeppelin.org[contact]. The team will do its best to assist and mitigate any potential misuses of the library. However, keep in mind the flexibility assumed for this repository may relax our assessment.
diff --git a/docs/modules/ROOT/pages/paymasters.adoc b/docs/modules/ROOT/pages/paymasters.adoc
deleted file mode 100644
index cf347e6c..00000000
--- a/docs/modules/ROOT/pages/paymasters.adoc
+++ /dev/null
@@ -1,506 +0,0 @@
-= 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.
-
-In the context of account abstraction, sponsoring user operations allows a third party to pay for transaction gas fees on behalf of users. This can improve user experience by eliminating the need for users to hold native cryptocurrency (like ETH) to pay for transactions.
-
-To enable sponsorship, users sign their user operations including a special field called `paymasterAndData`, resulting from the concatenation of the paymaster address they're intending to use and the associated calldata that's going to be passed into xref:api:utils/cryptography.adoc#PaymasterCore-validatePaymasterUserOp[`validatePaymasterUserOp`]. The EntryPoint will use this field to determine whether it is willing to pay for the user operation or not.
-
-== Signed Sponsorship
-
-The xref:api:account.adoc#PaymasterSigner[`PaymasterSigner`] implements signature-based sponsorship via authorization signatures, allowing designated paymaster signers to authorize and sponsor specific user operations without requiring users to hold native ETH.
-
-TIP: Learn more about xref:accounts.adoc#selecting_a_signer[signers] to explore different approaches to user operation sponsorship via signatures.
-
-[source,solidity]
-----
-include::api:example$account/paymaster/PaymasterECDSASigner.sol[]
-----
-
-TIP: Use https://docs.openzeppelin.com/contracts/5.x/api/account#ERC4337Utils[`ERC4337Utils`] to facilitate the access to paymaster-related fields of the userOp (e.g. `paymasterData`, `paymasterVerificationGasLimit`)
-
-To implement signature-based sponsorship, you'll first need to deploy the paymaster contract. This contract will hold the ETH used to pay for user operations and verify signatures from your authorized signer. After deployment, you must fund the paymaster with ETH to cover gas costs for the operations it will sponsor:
-
-[source,typescript]
-----
-// Fund the paymaster with ETH
-await eoaClient.sendTransaction({
- to: paymasterECDSASigner.address,
- value: parseEther("0.01"),
- data: encodeFunctionData({
- abi: paymasterECDSASigner.abi,
- functionName: "deposit",
- args: [],
- }),
-});
-----
-
-WARNING: Paymasters require sufficient ETH balance to pay for gas costs. If the paymaster runs out of funds, all operations it's meant to sponsor will fail. Consider implementing monitoring and automatic refilling of the paymaster's balance in production environments.
-
-When a user initiates an operation that requires sponsorship, your backend service (or other authorized entity) needs to sign the operation using EIP-712. This signature proves to the paymaster that it should cover the gas costs for this specific user operation:
-
-[source,typescript]
-----
-// Set validation window
-const now = Math.floor(Date.now() / 1000);
-const validAfter = now - 60; // Valid from 1 minute ago
-const validUntil = now + 3600; // Valid for 1 hour
-const paymasterVerificationGasLimit = 100_000n;
-const paymasterPostOpGasLimit = 300_000n;
-
-// Sign using EIP-712 typed data
-const paymasterSignature = await signer.signTypedData({
- domain: {
- chainId: await signerClient.getChainId(),
- name: "MyPaymasterECDSASigner",
- verifyingContract: paymasterECDSASigner.address,
- version: "1",
- },
- types: {
- UserOperationRequest: [
- { name: "sender", type: "address" },
- { name: "nonce", type: "uint256" },
- { name: "initCode", type: "bytes" },
- { name: "callData", type: "bytes" },
- { name: "accountGasLimits", type: "bytes32" },
- { name: "preVerificationGas", type: "uint256" },
- { name: "gasFees", type: "bytes32" },
- { name: "paymasterVerificationGasLimit", type: "uint256" },
- { name: "paymasterPostOpGasLimit", type: "uint256" },
- { name: "validAfter", type: "uint48" },
- { name: "validUntil", type: "uint48" },
- ],
- },
- primaryType: "UserOperationRequest",
- message: {
- sender: userOp.sender,
- nonce: userOp.nonce,
- initCode: userOp.initCode,
- callData: userOp.callData,
- accountGasLimits: userOp.accountGasLimits,
- preVerificationGas: userOp.preVerificationGas,
- gasFees: userOp.gasFees,
- paymasterVerificationGasLimit,
- paymasterPostOpGasLimit,
- validAfter,
- validUntil,
- },
-});
-----
-
-The time window (`validAfter` and `validUntil`) prevents replay attacks and allows you to limit how long the signature remains valid. Once signed, the paymaster data needs to be formatted and attached to the user operation:
-
-[source,typescript]
-----
-userOp.paymasterAndData = encodePacked(
- ["address", "uint128", "uint128", "bytes"],
- [
- paymasterECDSASigner.address,
- paymasterVerificationGasLimit,
- paymasterPostOpGasLimit,
- encodePacked(
- ["uint48", "uint48", "bytes"],
- [validAfter, validUntil, paymasterSignature]
- ),
- ]
-);
-----
-
-TIP: The `paymasterVerificationGasLimit` and `paymasterPostOpGasLimit` values should be adjusted based on your paymaster's complexity. Higher values increase the gas cost but provide more execution headroom, reducing the risk of out-of-gas errors during validation or post-operation processing.
-
-With the paymaster data attached, the user operation can now be signed by the account signer and submitted to the EntryPoint contract:
-
-[source,typescript]
-----
-// Sign the user operation with the account owner
-const signedUserOp = await signUserOp(entrypoint, userOp);
-
-// Submit to the EntryPoint contract
-const userOpReceipt = await eoaClient.writeContract({
- abi: EntrypointV08Abi,
- address: entrypoint.address,
- functionName: "handleOps",
- args: [[signedUserOp], beneficiary.address],
-});
-----
-
-Behind the scenes, the EntryPoint will call the paymaster's `validatePaymasterUserOp` function, which verifies the signature and time window. If valid, the paymaster commits to paying for the operation's gas costs, and the EntryPoint executes the operation.
-
-== ERC20-based Sponsorship
-
-While signature-based sponsorship is useful for many applications, sometimes you want users to pay for their own transactions but using tokens instead of ETH. The xref:api:account.adoc#PaymasterERC20[`PaymasterERC20`] allows users to pay for gas fees using ERC-20 tokens. Developers must implement an xref:api:account.adoc#PaymasterERC20-_fetchDetails-struct-PackedUserOperation-bytes32-[`_fetchDetails`] to get the token price information from an oracle of their preference.
-
-[source,solidity]
-----
-function _fetchDetails(
- PackedUserOperation calldata userOp,
- bytes32 userOpHash
-) internal view override returns (uint256 validationData, IERC20 token, uint256 tokenPrice) {
- // Implement logic to fetch the token and token price from the userOp
-}
-----
-
-=== Using Oracles
-
-==== Chainlink Price Feeds
-
-A popular approach to implement price oracles is to use https://docs.chain.link/data-feeds/using-data-feeds[Chainlink's price feeds]. By using their https://docs.chain.link/data-feeds/api-reference#aggregatorv3interface[`AggregatorV3Interface`] developers determine the token-to-ETH exchange rate dynamically for their paymasters. This ensures fair pricing even as market rates fluctuate.
-
-Consider the following contract:
-
-[source,solidity]
-----
-// WARNING: Unaudited code.
-// Consider performing a security review before going to production.
-contract PaymasterUSDCChainlink is PaymasterERC20, Ownable {
- // Values for sepolia
- // See https://docs.chain.link/data-feeds/price-feeds/addresses
- AggregatorV3Interface public constant USDC_USD_ORACLE =
- AggregatorV3Interface(0xA2F78ab2355fe2f984D808B5CeE7FD0A93D5270E);
- AggregatorV3Interface public constant ETH_USD_ORACLE =
- AggregatorV3Interface(0x694AA1769357215DE4FAC081bf1f309aDC325306);
-
- // See https://sepolia.etherscan.io/token/0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238
- IERC20 private constant USDC =
- IERC20(0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238);
-
- constructor(address initialOwner) Ownable(initialOwner) {}
-
- function _authorizeWithdraw() internal virtual override onlyOwner {}
-
- function liveness() public view virtual returns (uint256) {
- return 15 minutes; // Tolerate stale data
- }
-
- function _fetchDetails(
- PackedUserOperation calldata userOp,
- bytes32 /* userOpHash */
- ) internal view virtual override returns (uint256 validationData, IERC20 token, uint256 tokenPrice) {
- (uint256 validationData_, uint256 price) = _fetchOracleDetails(userOp);
- return (
- validationData_,
- USDC,
- price
- );
- }
-
- function _fetchOracleDetails(
- PackedUserOperation calldata /* userOp */
- )
- internal
- view
- virtual
- returns (uint256 validationData, uint256 tokenPrice)
- {
- // ...
- }
-}
-----
-
-NOTE: The `PaymasterUSDCChainlink` contract uses specific Chainlink price feeds (ETH/USD and USDC/USD) on Sepolia. For production use or other networks, you'll need to modify the contract to use the appropriate price feed addresses.
-
-As you can see, a `_fetchOracleDetails` function is specified to fetch the token price that will be used as a reference for calculating the final ERC-20 payment. One can fetch and process price data from Chainlink oracles to determine the exchange rate between the price of a concrete ERC-20 and ETH. An example with USDC would be:
-
-1. Fetch the current `ETH/USD` and `USDC/USD` prices from their respective oracles.
-2. Calculate the `USDC/ETH` exchange rate using the formula: `USDC/ETH = (USDC/USD) / (ETH/USD)`. This gives us how many USDC tokens are needed to buy 1 ETH
-
-NOTE: The price of the ERC-20 must be scaled by xref:api:account.adoc#PaymasterERC20-_tokenPriceDenominator--[`_tokenPriceDenominator`].
-
-Here's how an implementation of `_fetchOracleDetails` would look like using this approach:
-
-TIP: Use https://docs.openzeppelin.com/contracts/5.x/api/account#ERC4337Utils-combineValidationData-uint256-uint256-[`ERC4337Utils.combineValidationData`] to merge two `validationData` values.
-
-[source,solidity]
-----
-// WARNING: Unaudited code.
-// Consider performing a security review before going to production.
-
-using SafeCast for *;
-using ERC4337Utils for *;
-
-function _fetchOracleDetails(
- PackedUserOperation calldata /* userOp */
-)
- internal
- view
- virtual
- returns (uint256 validationData, uint256 tokenPrice)
-{
- (uint256 ETHUSDValidationData, int256 ETHUSD) = _fetchPrice(
- ETH_USD_ORACLE
- );
- (uint256 USDCUSDValidationData, int256 USDCUSD) = _fetchPrice(
- USDC_USD_ORACLE
- );
-
- if (ETHUSD <= 0 || USDCUSD <= 0) {
- // No negative prices
- return (ERC4337Utils.SIG_VALIDATION_FAILED, 0);
- }
-
- // eth / usdc = (usdc / usd) / (eth / usd) = usdc * usd / eth * usd = usdc / eth
- int256 scale = _tokenPriceDenominator().toInt256();
- int256 scaledUSDCUSD = USDCUSD * scale * (10 ** ETH_USD_ORACLE.decimals()).toInt256();
- int256 scaledUSDCETH = scaledUSDCUSD / (ETHUSD * (10 ** USDC_USD_ORACLE.decimals()).toInt256());
-
- return (
- ETHUSDValidationData.combineValidationData(USDCUSDValidationData),
- uint256(scaledUSDCETH) // Safe upcast
- );
-}
-
-function _fetchPrice(
- AggregatorV3Interface oracle
-) internal view virtual returns (uint256 validationData, int256 price) {
- (
- uint80 roundId,
- int256 price_,
- ,
- uint256 timestamp,
- uint80 answeredInRound
- ) = oracle.latestRoundData();
- if (
- price_ == 0 || // No data
- answeredInRound < roundId || // Not answered in round
- timestamp == 0 || // Incomplete round
- block.timestamp - timestamp > liveness() // Stale data
- ) {
- return (ERC4337Utils.SIG_VALIDATION_FAILED, 0);
- }
- return (ERC4337Utils.SIG_VALIDATION_SUCCESS, price_);
-}
-----
-
-NOTE: An important difference with token-based sponsorship is that the user's smart account must first approve the paymaster to spend their tokens. You might want to incorporate this approval as part of your account initialization process, or check if approval is needed before executing an operation.
-
-The PaymasterERC20 contract follows a pre-charge and refund model:
-
-1. During validation, it pre-charges the maximum possible gas cost
-2. After execution, it refunds any unused gas back to the user
-
-This model ensures the paymaster can always cover gas costs, while only charging users for the actual gas used.
-
-[source,typescript]
-----
-const paymasterVerificationGasLimit = 150_000n;
-const paymasterPostOpGasLimit = 300_000n;
-
-userOp.paymasterAndData = encodePacked(
- ["address", "uint128", "uint128", "bytes"],
- [
- paymasterUSDCChainlink.address,
- paymasterVerificationGasLimit,
- paymasterPostOpGasLimit,
- "0x" // No additional data needed
- ]
-);
-----
-
-For the rest, you can sign the user operation as you would normally do once the `paymasterAndData` field has been set.
-
-[source,typescript]
-----
-// Sign the user operation with the account owner
-const signedUserOp = await signUserOp(entrypoint, userOp);
-
-// Submit to the EntryPoint contract
-const userOpReceipt = await eoaClient.writeContract({
- abi: EntrypointV08Abi,
- address: entrypoint.address,
- functionName: "handleOps",
- args: [[signedUserOp], beneficiary.address],
-});
-----
-
-WARNING: Oracle-based pricing relies on the accuracy and freshness of price feeds. The `PaymasterUSDCChainlink` includes safety checks for stale data, but you should still monitor for extreme market volatility that could affect your users.
-
-=== Using a Guarantor
-
-There are multiple valid cases where the user might not have enough tokens to pay for the transaction before it takes place. For example, if the user is claiming an airdrop, they might need their first transaction to be sponsored. For those cases, the xref:api:account.adoc#PaymasterERC20Guarantor[`PaymasterERC20Guarantor`] contract extends the standard PaymasterERC20 to allow a third party (guarantor) to back user operations.
-
-The guarantor pre-funds the maximum possible gas cost upfront, and after execution:
-
-1. If the user repays the guarantor, the guarantor gets their funds back
-2. If the user fails to repay, the guarantor absorbs the cost
-
-[TIP]
-====
-A common use case is for guarantors to pay for operations of users claiming airdrops:
-
-* The guarantor pays gas fees upfront
-* The user claims their airdrop tokens
-* The user repays the guarantor from the claimed tokens
-* If the user fails to repay, the guarantor absorbs the cost
-====
-
-To implement guarantor functionality, your paymaster needs to extend the PaymasterERC20Guarantor class and implement the `_fetchGuarantor` function:
-
-[source,solidity]
-----
-function _fetchGuarantor(
- PackedUserOperation calldata userOp
-) internal view override returns (address guarantor) {
- // Implement logic to fetch and validate the guarantor from userOp
-}
-----
-
-Let's create a guarantor-enabled paymaster by extending our previous example:
-
-```solidity
-// WARNING: Unaudited code.
-// Consider performing a security review before going to production.
-contract PaymasterUSDCGuaranteed is EIP712, PaymasterERC20Guarantor, Ownable {
-
- // Keep the same oracle code as before...
-
- bytes32 private constant GUARANTEED_USER_OPERATION_TYPEHASH =
- keccak256(
- "GuaranteedUserOperation(address sender,uint256 nonce,bytes initCode,bytes callData,bytes32 accountGasLimits,uint256 preVerificationGas,bytes32 gasFees,bytes paymasterData)"
- );
-
- constructor(
- address initialOwner
- ) EIP712("PaymasterUSDCGuaranteed", "1") Ownable(initialOwner) {}
-
- // Other functions from PaymasterUSDCChainlink...
-
- function _fetchGuarantor(
- PackedUserOperation calldata userOp
- ) internal view override returns (address guarantor) {
- bytes calldata paymasterData = userOp.paymasterData();
-
- // Check guarantor data (should be at least 22 bytes: 20 for address + 2 for sig length)
- // If no guarantor specified, return early
- if (paymasterData.length < 22 || guarantor == address(0)) {
- return address(0);
- }
-
- guarantor = address(bytes20(paymasterData[:20]));
- uint16 guarantorSigLength = uint16(bytes2(paymasterData[20:22]));
-
- // Ensure the signature fits in the data
- if (paymasterData.length < 22 + guarantorSigLength) {
- return address(0);
- }
-
- bytes calldata guarantorSignature = paymasterData[22:22 + guarantorSigLength];
-
- // Validate the guarantor's signature
- bytes32 structHash = _getGuaranteedOperationStructHash(userOp);
- bytes32 hash = _hashTypedDataV4(structHash);
-
- return SignatureChecker.isValidSignatureNow(
- guarantor,
- hash,
- guarantorSignature
- ) ? guarantor : address(0);
- }
-
- function _getGuaranteedOperationStructHash(
- PackedUserOperation calldata userOp
- ) internal pure returns (bytes32) {
- return keccak256(
- abi.encode(
- GUARANTEED_USER_OPERATION_TYPEHASH,
- userOp.sender,
- userOp.nonce,
- keccak256(userOp.initCode),
- keccak256(userOp.callData),
- userOp.accountGasLimits,
- userOp.preVerificationGas,
- userOp.gasFees,
- keccak256(bytes(userOp.paymasterData()[:20])) // Just the guarantor address part
- )
- );
- }
-}
-```
-
-With this implementation, a guarantor would sign a user operation to authorize backing it:
-
-[source,typescript]
-----
-// Sign the user operation with the guarantor
-const guarantorSignature = await guarantor.signTypedData({
- domain: {
- chainId: await guarantorClient.getChainId(),
- name: "PaymasterUSDCGuaranteed",
- verifyingContract: paymasterUSDC.address,
- version: "1",
- },
- types: {
- GuaranteedUserOperation: [
- { name: "sender", type: "address" },
- { name: "nonce", type: "uint256" },
- { name: "initCode", type: "bytes" },
- { name: "callData", type: "bytes" },
- { name: "accountGasLimits", type: "bytes32" },
- { name: "preVerificationGas", type: "uint256" },
- { name: "gasFees", type: "bytes32" },
- { name: "paymasterData", type: "bytes" }
- ]
- },
- primaryType: "GuaranteedUserOperation",
- message: {
- sender: userOp.sender,
- nonce: userOp.nonce,
- initCode: userOp.initCode,
- callData: userOp.callData,
- accountGasLimits: userOp.accountGasLimits,
- preVerificationGas: userOp.preVerificationGas,
- gasFees: userOp.gasFees,
- paymasterData: guarantorAddress // Just the guarantor address
- },
-});
-----
-
-Then, we include the guarantor's address and its signature in the paymaster data:
-
-[source,typescript]
-----
-const paymasterVerificationGasLimit = 150_000n;
-const paymasterPostOpGasLimit = 300_000n;
-
-userOp.paymasterAndData = encodePacked(
- ["address", "uint128", "uint128", "bytes"],
- [
- paymasterUSDC.address,
- paymasterVerificationGasLimit,
- paymasterPostOpGasLimit,
- encodePacked(
- ["address", "bytes2", "bytes"],
- [
- guarantorAddress,
- toHex(guarantorSignature.replace("0x", "").length / 2, { size: 2 }),
- guarantorSignature
- ]
- )
- ]
-);
-----
-
-When the operation executes:
-
-1. During validation, the paymaster verifies the guarantor's signature and pre-funds from the guarantor's account
-2. The user operation executes, potentially giving the user tokens (like in an airdrop claim)
-3. During post-operation, the paymaster first tries to get repayment from the user
-4. If the user can't pay, the guarantor's pre-funded amount is used
-5. An event is emitted indicating who ultimately paid for the operation
-
-This approach enables novel use cases where users don't need tokens to start using a web3 app, and can cover costs after receiving value through their transaction.
-
-== Practical Considerations
-
-When implementing paymasters in production environments, keep these considerations in mind:
-
-1. **Balance management**: Regularly monitor and replenish your paymaster's ETH balance to ensure uninterrupted service.
-
-2. **Gas limits**: The verification and post-operation gas limits should be set carefully. Too low, and operations might fail; too high, and you waste resources.
-
-3. **Security**: For signature-based paymasters, protect your signing key as it controls who gets subsidized operations.
-
-4. **Price volatility**: For token-based paymasters, consider restricting which tokens are accepted, and implementing circuit breakers for extreme market conditions.
-
-5. **Spending limits**: Consider implementing daily or per-user limits to prevent abuse of your paymaster.
-
-TIP: For production deployments, it's often useful to implement a monitoring service that tracks paymaster usage, balances, and other metrics to ensure smooth operation.
diff --git a/docs/modules/ROOT/pages/utilities.adoc b/docs/modules/ROOT/pages/utilities.adoc
deleted file mode 100644
index e5bf38c3..00000000
--- a/docs/modules/ROOT/pages/utilities.adoc
+++ /dev/null
@@ -1,61 +0,0 @@
-= 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 xref:api:utils.adoc[API Reference].
-
-== Cryptography
-
-=== Validating Typed Data Signatures
-
-_For prior knowledge on how to validate signatures on-chain, check out the https://docs.openzeppelin.com/contracts/5.x/utilities#checking_signatures_on_chain[OpenZeppelin Contracts documentation]_
-
-As opposed to validating plain-text messages, it is possible to let your users sign structured data (i.e. typed values) in a way that is still readable on their wallets. This is possible by implementing https://docs.openzeppelin.com/contracts/api/utils#EIP712[`EIP712`], a standard way to encode structured data into a typed data hash.
-
-To start validating signed typed structures, just validate the https://docs.openzeppelin.com/contracts/api/utils#EIP712-_hashTypedDataV4-bytes32-[typed data hash]:
-
-[source,solidity]
-----
-include::api:example$utils/cryptography/MyContractDomain.sol[]
-----
-
-As part of the message, EIP-712 requires implementers to include a domain separator, which is a hash that includes the current smart contract address and the chain id where it's deployed. This way, the smart contract can be sure that the structured message was signed for its specific domain, avoiding replayability of signatures in smart contracts.
-
-==== Validating Nested EIP-712 Signatures
-
-Accounts (i.e. Smart Contract Wallets or Smart Accounts) are particularly likely to be controlled by multiple signers. As such, it's important to make sure that signatures are:
-
-1. Only valid for the intended domain and account.
-2. Validated in a way that's readable for the end signer.
-
-On one hand, making sure that the Account signature is only valid for an specific smart contract (i.e. an application) is difficult since it requires to validate a signature whose domain is the application but also the Account itself. For these reason, the community developed https://eips.ethereum.org/EIPS/eip-7739[ERC-7739]; a defensive rehashing mechanism that binds a signature to a single domain using a nested EIP-712 approach (i.e. an EIP-712 typed structure wrapping another).
-
-In case your smart contract validates signatures, using https://docs.openzeppelin.com/contracts/api/utils/cryptography#ERC7739[`ERC7739`] signer will implement the https://docs.openzeppelin.com/contracts/api/interfaces#IERC1271[`IERC1271`] interface for validating smart contract signatures following the approach suggested by ERC-7739:
-
-[source,solidity]
-----
-include::api:example$utils/cryptography/ERC7739SignerECDSA.sol[]
-----
-
-=== ERC-7913 Signature Verifiers
-
-ERC-7913 extends the concept of signature verification to support keys that don't have their own Ethereum address. This is particularly useful for integrating non-Ethereum cryptographic curves, hardware devices, or other identity systems into smart accounts.
-
-The standard defines a verifier interface that can be implemented to support different types of keys. A signer is represented as a `bytes` object that concatenates a verifier address and a key: `verifier || key`.
-
-xref:api:utils/cryptography.adoc#ERC7913Utils[`ERC7913Utils`] provides functions for verifying signatures using ERC-7913 compatible verifiers:
-
-[source,solidity]
-----
-using ERC7913Utils for bytes;
-
-function _verify(bytes memory signer, bytes32 hash, bytes memory signature) internal view returns (bool) {
- return signer.isValidSignatureNow(hash, signature);
-}
-----
-
-The verification process works as follows:
-
-* If `signer.length < 20`: verification fails
-* If `signer.length == 20`: verification is done using https://docs.openzeppelin.com/contracts/5.x/api/utils#SignatureChecker[SignatureChecker]
-* Otherwise: verification is done using an ERC-7913 verifier.
-
-This allows for backward compatibility with EOAs and ERC-1271 contracts while supporting new types of keys.
diff --git a/docs/templates/contract.hbs b/docs/templates/contract.hbs
index bcf6bfd0..d4bc0a71 100644
--- a/docs/templates/contract.hbs
+++ b/docs/templates/contract.hbs
@@ -1,137 +1,158 @@
-{{#each items}}
-:{{name}}: pass:normal[xref:#{{anchor}}[`++{{name}}++`]]
-{{/each}}
+{{reset-function-counts}}
+
+
{{/each}}
diff --git a/docs/templates/helpers.js b/docs/templates/helpers.js
index 1b638354..b4a32d79 100644
--- a/docs/templates/helpers.js
+++ b/docs/templates/helpers.js
@@ -1,13 +1,61 @@
const { version } = require('../../package.json');
+const fs = require('fs');
+
+const PATH_PREFIX = '/community-contracts/api/';
module.exports['oz-version'] = () => version;
module.exports['readme-path'] = opts => {
- return 'contracts/' + opts.data.root.id.replace(/\.adoc$/, '') + '/README.adoc';
+ const pageId = opts.data.root.id;
+ const basePath = pageId.replace(/\.(adoc|mdx)$/, '');
+ return 'contracts/' + basePath + '/README.mdx';
+};
+
+module.exports.readme = readmePath => {
+ try {
+ if (fs.existsSync(readmePath)) {
+ const readmeContent = fs.readFileSync(readmePath, 'utf8');
+ return processMdxContent(readmeContent);
+ }
+ } catch (error) {
+ console.warn(`Warning: Could not process README at ${readmePath}:`, error.message);
+ }
+ return '';
};
module.exports.names = params => params?.map(p => p.name).join(', ');
+// Simple function counter for unique IDs
+const functionNameCounts = {};
+
+module.exports['simple-id'] = function (name) {
+ if (!functionNameCounts[name]) {
+ functionNameCounts[name] = 1;
+ return name;
+ } else {
+ functionNameCounts[name]++;
+ return `${name}-${functionNameCounts[name]}`;
+ }
+};
+
+module.exports['reset-function-counts'] = function () {
+ Object.keys(functionNameCounts).forEach(key => delete functionNameCounts[key]);
+ return '';
+};
+
+module.exports.eq = (a, b) => a === b;
+module.exports['starts-with'] = (str, prefix) => str && str.startsWith(prefix);
+
+// Process natspec content with {REF} and link replacement
+module.exports['process-natspec'] = function (natspec, opts) {
+ if (!natspec) return '';
+
+ const currentPage = opts.data.root.__item_context?.page || opts.data.root.id;
+ const links = getAllLinks(opts.data.site.items, currentPage);
+
+ return processReferences(natspec, links);
+};
+
module.exports['typed-params'] = params => {
return params?.map(p => `${p.type}${p.indexed ? ' indexed' : ''}${p.name ? ' ' + p.name : ''}`).join(', ');
};
@@ -19,28 +67,220 @@ const slug = (module.exports.slug = str => {
return str.replace(/\W/g, '-');
});
+// Link generation and caching
const linksCache = new WeakMap();
-function getAllLinks(items) {
- if (linksCache.has(items)) {
- return linksCache.get(items);
+function getAllLinks(items, currentPage) {
+ if (currentPage) {
+ const cacheKey = currentPage;
+ let cache = linksCache.get(items);
+ if (!cache) {
+ cache = new Map();
+ linksCache.set(items, cache);
+ }
+
+ if (cache.has(cacheKey)) {
+ return cache.get(cacheKey);
+ }
}
+
const res = {};
- linksCache.set(items, res);
+ const currentPagePath = currentPage ? currentPage.replace(/\.mdx$/, '') : '';
+
for (const item of items) {
- res[`xref-${item.anchor}`] = `xref:${item.__item_context.page}#${item.anchor}`;
- res[slug(item.fullName)] = `pass:normal[xref:${item.__item_context.page}#${item.anchor}[\`${item.fullName}\`]]`;
+ const pagePath = item.__item_context.page.replace(/\.mdx$/, '');
+ const linkPath = generateLinkPath(pagePath, currentPagePath, item.anchor);
+
+ res[slug(item.fullName)] = `[\`${item.fullName}\`](${linkPath})`;
+ }
+
+ if (currentPage) {
+ let cache = linksCache.get(items);
+ if (!cache) {
+ cache = new Map();
+ linksCache.set(items, cache);
+ }
+ cache.set(currentPage, res);
}
+
return res;
}
+function generateLinkPath(pagePath, currentPagePath, anchor) {
+ if (
+ currentPagePath &&
+ (pagePath === currentPagePath || pagePath.split('/').pop() === currentPagePath.split('/').pop())
+ ) {
+ return `#${anchor}`;
+ }
+
+ if (currentPagePath) {
+ const currentParts = currentPagePath.split('/');
+ const targetParts = pagePath.split('/');
+
+ // Find common base
+ let i = 0;
+ while (i < currentParts.length && i < targetParts.length && currentParts[i] === targetParts[i]) {
+ i++;
+ }
+
+ const upLevels = Math.max(0, currentParts.length - 1 - i);
+ const downPath = targetParts.slice(i);
+
+ if (upLevels === 0 && downPath.length === 1) {
+ return `${PATH_PREFIX}${downPath[0]}#${anchor}`;
+ } else if (upLevels === 0) {
+ return `${PATH_PREFIX}${downPath.join('/')}#${anchor}`;
+ } else {
+ const relativePath = downPath.join('/');
+ return `${PATH_PREFIX}${relativePath}#${anchor}`;
+ }
+ }
+
+ return `${PATH_PREFIX}${pagePath}#${anchor}`;
+}
+
+// Process {REF} and other references
+function processReferences(content, links) {
+ let result = content;
+
+ // Handle {REF:Contract.method} patterns
+ result = result.replace(/\{REF:([^}]+)\}/g, (match, refId) => {
+ const resolvedRef = resolveReference(refId, links);
+ return resolvedRef || match;
+ });
+
+ // Replace {link-key} placeholders with markdown links
+ result = result.replace(/\{([-._a-z0-9]+)\}/gi, (match, key) => {
+ const replacement = findBestMatch(key, links);
+ return replacement || `\`${key}\``;
+ });
+
+ // Handle standalone contract names on their own line (e.g., "ERC7579Executor")
+ // This matches lines with just a contract name (starts with capital letter)
+ result = result.replace(/^([A-Z][a-zA-Z0-9]+)$/gm, (match, contractName) => {
+ const replacement = findBestMatch(contractName, links);
+ return replacement || match;
+ });
+
+ return cleanupContent(result);
+}
+
+function resolveReference(refId, links) {
+ // Try fuzzy matching for fullName keys
+ const matchingKeys = Object.keys(links).filter(key => {
+ const normalizedKey = key.toLowerCase();
+ const normalizedRef = refId.replace(/\./g, '-').toLowerCase();
+ return normalizedKey.includes(normalizedRef) || normalizedRef.includes(normalizedKey);
+ });
+
+ if (matchingKeys.length > 0) {
+ const bestMatch = matchingKeys[0];
+ const parts = refId.split('.');
+ const displayText = parts.length > 1 ? `${parts[0]}.${parts[1]}` : refId;
+ return `[\`${displayText}\`](${links[bestMatch]})`;
+ }
+
+ return null;
+}
+
+function findBestMatch(key, links) {
+ let replacement = links[key];
+
+ if (!replacement) {
+ // Strategy 1: Look for keys that end with this key
+ let matchingKeys = Object.keys(links).filter(linkKey => {
+ const parts = linkKey.split('-');
+ return parts.length >= 2 && parts[parts.length - 1] === key;
+ });
+
+ // Strategy 2: Try with different separators
+ if (matchingKeys.length === 0) {
+ const keyWithDashes = key.replace(/\./g, '-');
+ matchingKeys = Object.keys(links).filter(linkKey => linkKey.includes(keyWithDashes));
+ }
+
+ // Strategy 3: Try partial matches
+ if (matchingKeys.length === 0) {
+ matchingKeys = Object.keys(links).filter(linkKey => {
+ return linkKey === key || linkKey.endsWith('-' + key) || linkKey.includes(key);
+ });
+ }
+
+ if (matchingKeys.length > 0) {
+ replacement = links[matchingKeys[0]];
+ }
+ }
+
+ return replacement;
+}
+
+function cleanupContent(content) {
+ return content
+ .replace(/</g, '<')
+ .replace(/>/g, '>')
+ .replace(/"/g, '"')
+ .replace(/'/g, "'")
+ .replace(///g, '/')
+ .replace(/`/g, '`')
+ .replace(/=/g, '=')
+ .replace(/&/g, '&')
+ .replace(/\{(\[`[^`]+`\]\([^)]+\))\}/g, '$1')
+ .replace(/https?:\/\/[^\s[]+\[[^\]]+\]/g, match => {
+ const urlMatch = match.match(/^(https?:\/\/[^[]+)\[([^\]]+)\]$/);
+ return urlMatch ? `[${urlMatch[2]}](${urlMatch[1]})` : match;
+ });
+}
+
+function processMdxContent(content) {
+ try {
+ // Process MDX content - strip frontmatter and cleanup
+ let mdxContent = content
+ // Remove frontmatter (--- ... ---)
+ .replace(/^---\s*\n[\s\S]*?\n---\s*\n/, '')
+ // Remove "better viewed at" callouts
+ .replace(
+ /\s*This document is better viewed at https:\/\/docs\.openzeppelin\.com[^\n]*\s*<\/Callout>\s*/g,
+ '',
+ )
+ // Ensure relative image paths start with /
+ .replace(/!\[([^\]]*)\]\(([^/)][^)]*\.(png|jpg|jpeg|gif|svg|webp))\)/g, '')
+ // Remove any title headers (they're in frontmatter now)
+ .replace(/^#+\s+.+$/m, '')
+ // Clean up leading newlines
+ .replace(/^\n+/, '');
+
+ return mdxContent;
+ } catch (error) {
+ console.warn('Warning: Failed to process MDX content:', error.message);
+ return content;
+ }
+}
+
+module.exports.title = opts => {
+ const pageId = opts.data.root.id;
+ const basePath = pageId.replace(/\.(adoc|mdx)$/, '');
+ const parts = basePath.split('/');
+ const dirName = parts[parts.length - 1] || 'Contracts';
+ return dirName
+ .split('-')
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1))
+ .join(' ');
+};
+
+module.exports.description = opts => {
+ const pageId = opts.data.root.id;
+ const basePath = pageId.replace(/\.(adoc|mdx)$/, '');
+ const parts = basePath.split('/');
+ const dirName = parts[parts.length - 1] || 'contracts';
+ return `Smart contract ${dirName.replace('-', ' ')} utilities and implementations`;
+};
+
module.exports['with-prelude'] = opts => {
- const links = getAllLinks(opts.data.site.items);
+ const currentPage = opts.data.root.id;
+ const links = getAllLinks(opts.data.site.items, currentPage);
const contents = opts.fn();
- const neededLinks = contents
- .match(/\{[-._a-z0-9]+\}/gi)
- .map(m => m.replace(/^\{(.+)\}$/, '$1'))
- .filter(k => k in links);
- const prelude = neededLinks.map(k => `:${k}: ${links[k]}`).join('\n');
- return prelude + '\n' + contents;
+
+ const processed = processReferences(contents, links);
+ return processed;
};
diff --git a/docs/templates/page.hbs b/docs/templates/page.hbs
index cab050ac..11c79513 100644
--- a/docs/templates/page.hbs
+++ b/docs/templates/page.hbs
@@ -1,4 +1,13 @@
-:github-icon: pass:[]
+---
+title: "{{title}}"
+description: "{{description}}"
+---
+
{{#with-prelude}}
{{readme (readme-path)}}
{{/with-prelude}}
+
+{{#each items}}
+{{>contract}}
+
+{{/each}}
diff --git a/docs/templates/properties.js b/docs/templates/properties.js
index 52eebac5..f2453b63 100644
--- a/docs/templates/properties.js
+++ b/docs/templates/properties.js
@@ -17,9 +17,26 @@ module.exports.anchor = function anchor({ item, contract }) {
return res;
};
+module.exports.fullname = function fullname({ item }) {
+ let res = '';
+ res += item.name;
+ if ('parameters' in item) {
+ const signature = item.parameters.parameters.map(v => v.typeName.typeDescriptions.typeString).join(',');
+ res += slug('(' + signature + ')');
+ }
+ if (isNodeType('VariableDeclaration', item)) {
+ res += '-' + slug(item.typeName.typeDescriptions.typeString);
+ }
+ if (res.charAt(res.length - 1) === '-') {
+ return res.slice(0, -1);
+ }
+ return res;
+};
+
module.exports.inheritance = function ({ item, build }) {
if (!isNodeType('ContractDefinition', item)) {
- throw new Error('used inherited-items on non-contract');
+ // Return empty array for non-contracts (interfaces, libraries)
+ return [];
}
return item.linearizedBaseContracts
@@ -28,19 +45,19 @@ module.exports.inheritance = function ({ item, build }) {
};
module.exports['has-functions'] = function ({ item }) {
- return item.inheritance.some(c => c.functions.length > 0);
+ return item.inheritance && item.inheritance.some(c => c.functions.length > 0);
};
module.exports['has-events'] = function ({ item }) {
- return item.inheritance.some(c => c.events.length > 0);
+ return item.inheritance && item.inheritance.some(c => c.events.length > 0);
};
module.exports['has-errors'] = function ({ item }) {
- return item.inheritance.some(c => c.errors.length > 0);
+ return item.inheritance && item.inheritance.some(c => c.errors.length > 0);
};
module.exports['internal-variables'] = function ({ item }) {
- return item.variables.filter(({ visibility }) => visibility === 'internal');
+ return item.variables ? item.variables.filter(({ visibility }) => visibility === 'internal') : [];
};
module.exports['has-internal-variables'] = function ({ item }) {
@@ -64,6 +81,8 @@ module.exports.returns2 = function ({ item }) {
module.exports['inherited-functions'] = function ({ item }) {
const { inheritance } = item;
+ if (!inheritance) return [];
+
const baseFunctions = new Set(inheritance.flatMap(c => c.functions.flatMap(f => f.baseFunctions ?? [])));
return inheritance.map((contract, i) => ({
contract,
diff --git a/netlify.toml b/netlify.toml
index 0447f41a..05acad15 100644
--- a/netlify.toml
+++ b/netlify.toml
@@ -1,3 +1,3 @@
[build]
-command = "npm run docs"
-publish = "build/site"
+command = "pnpm run docs"
+publish = "docs/oz-docs/out"
diff --git a/package.json b/package.json
index fc9c6537..59e78377 100644
--- a/package.json
+++ b/package.json
@@ -11,10 +11,11 @@
"scripts": {
"compile": "hardhat compile",
"clean": "hardhat clean && rimraf build contracts/build",
- "docs": "npm run prepare-docs && oz-docs",
- "docs:watch": "oz-docs watch contracts docs/templates docs/config.js",
+ "docs": "npm run prepare-docs && npm run oz-docs",
+ "docs:watch": "WATCH=true npm run docs",
"prepare": "husky",
"prepare-docs": "scripts/prepare-docs.sh",
+ "oz-docs": "scripts/oz-docs.sh",
"lint": "npm run lint:js && npm run lint:sol",
"lint:fix": "npm run lint:js:fix && npm run lint:sol:fix",
"lint:js": "prettier --log-level warn '**/*.{js,ts}' --check && eslint . --ignore-pattern lib/",
diff --git a/scripts/gen-nav.js b/scripts/gen-nav.js
deleted file mode 100644
index 6ac44560..00000000
--- a/scripts/gen-nav.js
+++ /dev/null
@@ -1,70 +0,0 @@
-#!/usr/bin/env node
-
-const path = require('path');
-const glob = require('glob');
-const startCase = require('lodash.startcase');
-
-const baseDir = process.argv[2];
-
-const files = glob.sync(baseDir + '/**/*.adoc').map(f => path.relative(baseDir, f));
-
-console.log('.API');
-
-function getPageTitle(directory) {
- return startCase(directory);
-}
-
-const menuItems = files.reduce(
- (acc, file) => {
- let current = acc;
- const doc = file.replace(baseDir, '');
-
- const keys = doc
- .split('/')
- .filter(Boolean)
- .map(k => k.replace('.adoc', ''));
-
- for (let i = 0; i < keys.length; i++) {
- current = current.items[keys[i]] ??= {
- name: startCase(keys[i]),
- dir: keys[i],
- items: {},
- doc,
- };
- }
-
- return acc;
- },
- {
- items: {},
- },
-);
-
-const arrayifyItems = items =>
- Object.entries(items).map(([k, v]) => {
- if (Object.keys(v.items ?? {}).length > 0) return [v, arrayifyItems(v.items)];
- return [k, v];
- });
-
-const isString = v => typeof v === 'string';
-
-const sortItems = items =>
- items.sort(([a], [b]) =>
- (isString(a) ? a : a.name).toLowerCase().localeCompare(isString(b) ? b : b.name, undefined, { numeric: true }),
- );
-
-const print = (items, level = 1) => {
- items.forEach(([k, v]) => {
- if (v.doc || k?.doc)
- console.log(`${'*'.repeat(level)} xref:${v.doc || k.doc}[${getPageTitle(isString(k) ? k : k.name)}]`);
- else console.log(`${'*'.repeat(level)} ${getPageTitle(isString(k) ? k : k.name)}`);
- if (Array.isArray(v)) print(v, level + 1);
- });
-};
-
-print(
- sortItems(arrayifyItems(menuItems.items)).map(([k, v]) => {
- if (v?.length > 0) return [k, sortItems(v)];
- return [k, v];
- }),
-);
diff --git a/scripts/generate/templates/EnumerableMapExtended.js b/scripts/generate/templates/EnumerableMapExtended.js
index 4e667d43..088887f8 100644
--- a/scripts/generate/templates/EnumerableMapExtended.js
+++ b/scripts/generate/templates/EnumerableMapExtended.js
@@ -9,7 +9,7 @@ import {EnumerableSetExtended} from "./EnumerableSetExtended.sol";
/**
* @dev Library for managing an enumerable variant of Solidity's
- * https://solidity.readthedocs.io/en/latest/types.html#mapping-types[\`mapping\`]
+ * [\`mapping\`](https://solidity.readthedocs.io/en/latest/types.html#mapping-types)
* type for non-value types as keys.
*
* Maps have the following properties:
@@ -34,17 +34,18 @@ import {EnumerableSetExtended} from "./EnumerableSetExtended.sol";
* - \`bytes -> uint256\` (\`BytesToUintMap\`)
* - \`string -> string\` (\`StringToStringMap\`)
*
- * [WARNING]
- * ====
+ *
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
- * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
+ * See [ethereum/solidity#11843](https://github.com/ethereum/solidity/pull/11843) for more info.
*
* In order to clean an EnumerableMap, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableMap.
- * ====
+ *
*
- * NOTE: Extensions of openzeppelin/contracts/utils/struct/EnumerableMap.sol.
+ *
+ * Extensions of openzeppelin/contracts/utils/struct/EnumerableMap.sol.
+ *
*/
`;
@@ -85,8 +86,10 @@ function remove(${name} storage map, ${key.typeLoc} key) internal returns (bool)
/**
* @dev Removes all the entries from a map. O(n).
*
- * WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
+ *
+ * Developers should keep in mind that this function has an unbounded cost and using it may render the
* function uncallable if the map grows to the point where clearing it consumes too much gas to fit in a block.
+ *
*/
function clear(${name} storage map) internal {
uint256 len = length(map);
@@ -158,10 +161,12 @@ function get(${name} storage map, ${key.typeLoc} key) internal view returns (${v
/**
* @dev Returns an array containing all the keys
*
- * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
+ *
+ * This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
+ *
*/
function keys(${name} storage map) internal view returns (${key.type}[] memory) {
return map._keys.values();
@@ -170,10 +175,12 @@ function keys(${name} storage map) internal view returns (${key.type}[] memory)
/**
* @dev Returns an array containing a slice of the keys
*
- * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
+ *
+ * This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
+ *
*/
function keys(${name} storage map, uint256 start, uint256 end) internal view returns (${key.type}[] memory) {
return map._keys.values(start, end);
diff --git a/scripts/generate/templates/EnumerableSetExtended.js b/scripts/generate/templates/EnumerableSetExtended.js
index a6c5827f..57931a8a 100644
--- a/scripts/generate/templates/EnumerableSetExtended.js
+++ b/scripts/generate/templates/EnumerableSetExtended.js
@@ -10,7 +10,7 @@ import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
/**
* @dev Library for managing
- * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of non-value
+ * [sets](https://en.wikipedia.org/wiki/Set_(abstract_data_type)) of non-value
* types.
*
* Sets have the following properties:
@@ -33,17 +33,18 @@ import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
* Sets of type \`string\` (\`StringSet\`), \`bytes\` (\`BytesSet\`) and
* \`bytes32[2]\` (\`Bytes32x2Set\`) are supported.
*
- * [WARNING]
- * ====
+ *
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
- * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
+ * See [ethereum/solidity#11843](https://github.com/ethereum/solidity/pull/11843) for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
- * ====
+ *
*
- * NOTE: This is an extension of openzeppelin/contracts/utils/struct/EnumerableSet.sol.
+ *
+ * This is an extension of openzeppelin/contracts/utils/struct/EnumerableSet.sol.
+ *
*/
`;
@@ -117,8 +118,10 @@ function remove(${name} storage self, ${value.type} memory value) internal retur
/**
* @dev Removes all the values from a set. O(n).
*
- * WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
+ *
+ * Developers should keep in mind that this function has an unbounded cost and using it may render the
* function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
+ *
*/
function clear(${name} storage set) internal {
uint256 len = length(set);
@@ -163,10 +166,12 @@ function at(${name} storage self, uint256 index) internal view returns (${value.
/**
* @dev Return the entire set in an array
*
- * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
+ *
+ * This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
+ *
*/
function values(${name} storage self) internal view returns (${value.type}[] memory) {
return self._values;
@@ -175,10 +180,12 @@ function values(${name} storage self) internal view returns (${value.type}[] mem
/**
* @dev Return a slice of the set in an array
*
- * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
+ *
+ * This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
+ *
*/
function values(${name} storage set, uint256 start, uint256 end) internal view returns (${value.type}[] memory) {
unchecked {
@@ -266,8 +273,10 @@ function remove(${name} storage self, ${value.type} memory value) internal retur
/**
* @dev Removes all the values from a set. O(n).
*
- * WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
+ *
+ * Developers should keep in mind that this function has an unbounded cost and using it may render the
* function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
+ *
*/
function clear(${name} storage self) internal {
${value.type}[] storage v = self._values;
@@ -312,10 +321,12 @@ function at(${name} storage self, uint256 index) internal view returns (${value.
/**
* @dev Return the entire set in an array
*
- * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
+ *
+ * This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
+ *
*/
function values(${name} storage self) internal view returns (${value.type}[] memory) {
return self._values;
@@ -324,10 +335,12 @@ function values(${name} storage self) internal view returns (${value.type}[] mem
/**
* @dev Return a slice of the set in an array
*
- * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
+ *
+ * This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
+ *
*/
function values(${name} storage set, uint256 start, uint256 end) internal view returns (${value.type}[] memory) {
unchecked {
diff --git a/scripts/oz-docs.sh b/scripts/oz-docs.sh
new file mode 100755
index 00000000..45fa50d9
--- /dev/null
+++ b/scripts/oz-docs.sh
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+cd $(dirname $0)/..
+ROOT=$(pwd)
+DOCS=$ROOT/docs
+OZ_DOCS=$DOCS/oz-docs
+rm -rf $OZ_DOCS
+cd $DOCS
+git clone --branch main https://github.com/OpenZeppelin/docs.git oz-docs
+cp -r $DOCS/modules/api/pages/* $OZ_DOCS/content/community-contracts/api
+cp -r $DOCS/modules/api/examples $OZ_DOCS/content/community-contracts/api
+cd $OZ_DOCS
+pnpm i
+if [ "${WATCH:-false}" = "true" ]; then
+ pnpm run dev
+else
+ pnpm run build
+fi
diff --git a/scripts/prepare-docs.sh b/scripts/prepare-docs.sh
index cc1fe376..c6e31c5c 100755
--- a/scripts/prepare-docs.sh
+++ b/scripts/prepare-docs.sh
@@ -22,5 +22,3 @@ for f in "$examples_source_dir"/**/*.sol; do
mkdir -p "$examples_target_dir/$(dirname "$name")"
sed -Ee '/^import/s|"(\.\./)+|"@openzeppelin/community-contracts/|' "$f" > "$examples_target_dir/$name"
done
-
-node scripts/gen-nav.js "$OUTDIR" > "$OUTDIR/../nav.adoc"