github-actions 2 سال پیش
والد
کامیت
18fd78ad7f

+ 3 - 5
.github/workflows/checks.yml

@@ -12,6 +12,9 @@ concurrency:
   group: checks-${{ github.ref }}
   cancel-in-progress: true
 
+env:
+  NODE_OPTIONS: --max_old_space_size=5120
+
 jobs:
   lint:
     runs-on: ubuntu-latest
@@ -25,7 +28,6 @@ jobs:
     runs-on: ubuntu-latest
     env:
       FORCE_COLOR: 1
-      NODE_OPTIONS: --max_old_space_size=4096
       GAS: true
     steps:
       - uses: actions/checkout@v3
@@ -56,8 +58,6 @@ jobs:
         run: bash scripts/upgradeable/transpile.sh
       - name: Run tests
         run: npm run test
-        env:
-          NODE_OPTIONS: --max_old_space_size=4096
       - name: Check linearisation of the inheritance graph
         run: npm run test:inheritance
       - name: Check storage layout
@@ -85,8 +85,6 @@ jobs:
       - name: Set up environment
         uses: ./.github/actions/setup
       - run: npm run coverage
-        env:
-          NODE_OPTIONS: --max_old_space_size=4096
       - uses: codecov/codecov-action@v3
         with:
           token: ${{ secrets.CODECOV_TOKEN }}

+ 88 - 0
contracts/mocks/docs/governance/MyGovernor.sol

@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.2;
+
+import "../../../governance/Governor.sol";
+import "../../../governance/compatibility/GovernorCompatibilityBravo.sol";
+import "../../../governance/extensions/GovernorVotes.sol";
+import "../../../governance/extensions/GovernorVotesQuorumFraction.sol";
+import "../../../governance/extensions/GovernorTimelockControl.sol";
+
+contract MyGovernor is
+    Governor,
+    GovernorCompatibilityBravo,
+    GovernorVotes,
+    GovernorVotesQuorumFraction,
+    GovernorTimelockControl
+{
+    constructor(
+        IVotes _token,
+        TimelockController _timelock
+    ) Governor("MyGovernor") GovernorVotes(_token) GovernorVotesQuorumFraction(4) GovernorTimelockControl(_timelock) {}
+
+    function votingDelay() public pure override returns (uint256) {
+        return 7200; // 1 day
+    }
+
+    function votingPeriod() public pure override returns (uint256) {
+        return 50400; // 1 week
+    }
+
+    function proposalThreshold() public pure override returns (uint256) {
+        return 0;
+    }
+
+    // The functions below are overrides required by Solidity.
+
+    function state(
+        uint256 proposalId
+    ) public view override(Governor, IGovernor, GovernorTimelockControl) returns (ProposalState) {
+        return super.state(proposalId);
+    }
+
+    function propose(
+        address[] memory targets,
+        uint256[] memory values,
+        bytes[] memory calldatas,
+        string memory description
+    ) public override(Governor, GovernorCompatibilityBravo, IGovernor) returns (uint256) {
+        return super.propose(targets, values, calldatas, description);
+    }
+
+    function cancel(
+        address[] memory targets,
+        uint256[] memory values,
+        bytes[] memory calldatas,
+        bytes32 descriptionHash
+    ) public override(Governor, GovernorCompatibilityBravo, IGovernor) returns (uint256) {
+        return super.cancel(targets, values, calldatas, descriptionHash);
+    }
+
+    function _execute(
+        uint256 proposalId,
+        address[] memory targets,
+        uint256[] memory values,
+        bytes[] memory calldatas,
+        bytes32 descriptionHash
+    ) internal override(Governor, GovernorTimelockControl) {
+        super._execute(proposalId, targets, values, calldatas, descriptionHash);
+    }
+
+    function _cancel(
+        address[] memory targets,
+        uint256[] memory values,
+        bytes[] memory calldatas,
+        bytes32 descriptionHash
+    ) internal override(Governor, GovernorTimelockControl) returns (uint256) {
+        return super._cancel(targets, values, calldatas, descriptionHash);
+    }
+
+    function _executor() internal view override(Governor, GovernorTimelockControl) returns (address) {
+        return super._executor();
+    }
+
+    function supportsInterface(
+        bytes4 interfaceId
+    ) public view override(Governor, IERC165, GovernorTimelockControl) returns (bool) {
+        return super.supportsInterface(interfaceId);
+    }
+}

+ 24 - 0
contracts/mocks/docs/governance/MyToken.sol

@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.2;
+
+import "../../../token/ERC20/ERC20.sol";
+import "../../../token/ERC20/extensions/ERC20Permit.sol";
+import "../../../token/ERC20/extensions/ERC20Votes.sol";
+
+contract MyToken is ERC20, ERC20Permit, ERC20Votes {
+    constructor() ERC20("MyToken", "MTK") ERC20Permit("MyToken") {}
+
+    // The functions below are overrides required by Solidity.
+
+    function _afterTokenTransfer(address from, address to, uint256 amount) internal override(ERC20, ERC20Votes) {
+        super._afterTokenTransfer(from, to, amount);
+    }
+
+    function _mint(address to, uint256 amount) internal override(ERC20, ERC20Votes) {
+        super._mint(to, amount);
+    }
+
+    function _burn(address account, uint256 amount) internal override(ERC20, ERC20Votes) {
+        super._burn(account, amount);
+    }
+}

+ 35 - 0
contracts/mocks/docs/governance/MyTokenTimestampBased.sol

@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.2;
+
+import "../../../token/ERC20/ERC20.sol";
+import "../../../token/ERC20/extensions/ERC20Permit.sol";
+import "../../../token/ERC20/extensions/ERC20Votes.sol";
+
+contract MyTokenTimestampBased is ERC20, ERC20Permit, ERC20Votes {
+    constructor() ERC20("MyTokenTimestampBased", "MTK") ERC20Permit("MyTokenTimestampBased") {}
+
+    // Overrides IERC6372 functions to make the token & governor timestamp-based
+
+    function clock() public view override returns (uint48) {
+        return uint48(block.timestamp);
+    }
+
+    // solhint-disable-next-line func-name-mixedcase
+    function CLOCK_MODE() public pure override returns (string memory) {
+        return "mode=timestamp";
+    }
+
+    // The functions below are overrides required by Solidity.
+
+    function _afterTokenTransfer(address from, address to, uint256 amount) internal override(ERC20, ERC20Votes) {
+        super._afterTokenTransfer(from, to, amount);
+    }
+
+    function _mint(address to, uint256 amount) internal override(ERC20, ERC20Votes) {
+        super._mint(to, amount);
+    }
+
+    function _burn(address account, uint256 amount) internal override(ERC20, ERC20Votes) {
+        super._burn(account, amount);
+    }
+}

+ 31 - 0
contracts/mocks/docs/governance/MyTokenWrapped.sol

@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.2;
+
+import "../../../token/ERC20/ERC20.sol";
+import "../../../token/ERC20/extensions/ERC20Permit.sol";
+import "../../../token/ERC20/extensions/ERC20Votes.sol";
+import "../../../token/ERC20/extensions/ERC20Wrapper.sol";
+
+contract MyTokenWrapped is ERC20, ERC20Permit, ERC20Votes, ERC20Wrapper {
+    constructor(
+        IERC20 wrappedToken
+    ) ERC20("MyTokenWrapped", "MTK") ERC20Permit("MyTokenWrapped") ERC20Wrapper(wrappedToken) {}
+
+    // The functions below are overrides required by Solidity.
+
+    function decimals() public pure override(ERC20, ERC20Wrapper) returns (uint8) {
+        return 18;
+    }
+
+    function _afterTokenTransfer(address from, address to, uint256 amount) internal override(ERC20, ERC20Votes) {
+        super._afterTokenTransfer(from, to, amount);
+    }
+
+    function _mint(address to, uint256 amount) internal override(ERC20, ERC20Votes) {
+        super._mint(to, amount);
+    }
+
+    function _burn(address account, uint256 amount) internal override(ERC20, ERC20Votes) {
+        super._burn(account, amount);
+    }
+}

+ 1 - 1
contracts/mocks/wizard/MyGovernor3.sol

@@ -7,7 +7,7 @@ import "../../governance/extensions/GovernorVotes.sol";
 import "../../governance/extensions/GovernorVotesQuorumFraction.sol";
 import "../../governance/extensions/GovernorTimelockControl.sol";
 
-contract MyGovernor is
+contract MyGovernor3 is
     Governor,
     GovernorTimelockControl,
     GovernorCompatibilityBravo,

+ 4 - 195
docs/modules/ROOT/pages/governance.adoc

@@ -43,82 +43,13 @@ In the rest of this guide, we will focus on a fresh deploy of the vanilla OpenZe
 The voting power of each account in our governance setup will be determined by an ERC20 token. The token has to implement the ERC20Votes extension. This extension will keep track of historical balances so that voting power is retrieved from past snapshots rather than current balance, which is an important protection that prevents double voting.
 
 ```solidity
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.2;
-
-import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
-import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
-import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";
-
-contract MyToken is ERC20, ERC20Permit, ERC20Votes {
-    constructor() ERC20("MyToken", "MTK") ERC20Permit("MyToken") {}
-
-    // The functions below are overrides required by Solidity.
-
-    function _afterTokenTransfer(address from, address to, uint256 amount)
-        internal
-        override(ERC20, ERC20Votes)
-    {
-        super._afterTokenTransfer(from, to, amount);
-    }
-
-    function _mint(address to, uint256 amount)
-        internal
-        override(ERC20, ERC20Votes)
-    {
-        super._mint(to, amount);
-    }
-
-    function _burn(address account, uint256 amount)
-        internal
-        override(ERC20, ERC20Votes)
-    {
-        super._burn(account, amount);
-    }
-}
+include::api:example$governance/MyToken.sol[]
 ```
 
 If your project already has a live token that does not include ERC20Votes and is not upgradeable, you can wrap it in a governance token by using ERC20Wrapper. This will allow token holders to participate in governance by wrapping their tokens 1-to-1.
 
 ```solidity
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.2;
-
-import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
-import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
-import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";
-import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Wrapper.sol";
-
-contract MyToken is ERC20, ERC20Permit, ERC20Votes, ERC20Wrapper {
-    constructor(IERC20 wrappedToken)
-        ERC20("MyToken", "MTK")
-        ERC20Permit("MyToken")
-        ERC20Wrapper(wrappedToken)
-    {}
-
-    // The functions below are overrides required by Solidity.
-
-    function _afterTokenTransfer(address from, address to, uint256 amount)
-        internal
-        override(ERC20, ERC20Votes)
-    {
-        super._afterTokenTransfer(from, to, amount);
-    }
-
-    function _mint(address to, uint256 amount)
-        internal
-        override(ERC20, ERC20Votes)
-    {
-        super._mint(to, amount);
-    }
-
-    function _burn(address account, uint256 amount)
-        internal
-        override(ERC20, ERC20Votes)
-    {
-        super._burn(account, amount);
-    }
-}
+include::api:example$governance/MyTokenWrapped.sol[]
 ```
 
 NOTE: The only other source of voting power available in OpenZeppelin Contracts currently is xref:api:token/ERC721.adoc#ERC721Votes[`ERC721Votes`]. ERC721 tokens that don't provide this functionality can be wrapped into a voting tokens using a combination of xref:api:token/ERC721.adoc#ERC721Votes[`ERC721Votes`] and xref:api:token/ERC721Wrapper.adoc#ERC721Wrapper[`ERC721Wrapper`].
@@ -146,87 +77,7 @@ These parameters are specified in the unit defined in the token's clock. Assumin
 We can optionally set a proposal threshold as well. This restricts proposal creation to accounts who have enough voting power.
 
 ```solidity
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.2;
-
-import "@openzeppelin/contracts/governance/Governor.sol";
-import "@openzeppelin/contracts/governance/compatibility/GovernorCompatibilityBravo.sol";
-import "@openzeppelin/contracts/governance/extensions/GovernorVotes.sol";
-import "@openzeppelin/contracts/governance/extensions/GovernorVotesQuorumFraction.sol";
-import "@openzeppelin/contracts/governance/extensions/GovernorTimelockControl.sol";
-
-contract MyGovernor is Governor, GovernorCompatibilityBravo, GovernorVotes, GovernorVotesQuorumFraction, GovernorTimelockControl {
-    constructor(IVotes _token, TimelockController _timelock)
-        Governor("MyGovernor")
-        GovernorVotes(_token)
-        GovernorVotesQuorumFraction(4)
-        GovernorTimelockControl(_timelock)
-    {}
-
-    function votingDelay() public pure override returns (uint256) {
-        return 7200; // 1 day
-    }
-
-    function votingPeriod() public pure override returns (uint256) {
-        return 50400; // 1 week
-    }
-
-    function proposalThreshold() public pure override returns (uint256) {
-        return 0;
-    }
-
-    // The functions below are overrides required by Solidity.
-
-    function state(uint256 proposalId)
-        public
-        view
-        override(Governor, IGovernor, GovernorTimelockControl)
-        returns (ProposalState)
-    {
-        return super.state(proposalId);
-    }
-
-    function propose(address[] memory targets, uint256[] memory values, bytes[] memory calldatas, string memory description)
-        public
-        override(Governor, GovernorCompatibilityBravo, IGovernor)
-        returns (uint256)
-    {
-        return super.propose(targets, values, calldatas, description);
-    }
-
-    function _execute(uint256 proposalId, address[] memory targets, uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash)
-        internal
-        override(Governor, GovernorTimelockControl)
-    {
-        super._execute(proposalId, targets, values, calldatas, descriptionHash);
-    }
-
-    function _cancel(address[] memory targets, uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash)
-        internal
-        override(Governor, GovernorTimelockControl)
-        returns (uint256)
-    {
-        return super._cancel(targets, values, calldatas, descriptionHash);
-    }
-
-    function _executor()
-        internal
-        view
-        override(Governor, GovernorTimelockControl)
-        returns (address)
-    {
-        return super._executor();
-    }
-
-    function supportsInterface(bytes4 interfaceId)
-        public
-        view
-        override(Governor, IERC165, GovernorTimelockControl)
-        returns (bool)
-    {
-        return super.supportsInterface(interfaceId);
-    }
-}
+include::api:example$governance/MyGovernor.sol[]
 ```
 
 === Timelock
@@ -338,49 +189,7 @@ Therefore, designing a timestamp based voting system starts with the token.
 Since v4.9, all voting contracts (including xref:api:token/ERC20.adoc#ERC20Votes[`ERC20Votes`] and xref:api:token/ERC721.adoc#ERC721Votes[`ERC721Votes`]) rely on xref:api:interfaces.adoc#IERC6372[IERC6372] for clock management. In order to change from operating with block numbers to operating with timestamps, all that is required is to override the `clock()` and `CLOCK_MODE()` functions.
 
 ```solidity
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.2;
-
-import "github.com/openzeppelin/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";
-import "github.com/openzeppelin/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Permit.sol";
-import "github.com/openzeppelin/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Votes.sol";
-
-contract MyToken is ERC20, ERC20Permit, ERC20Votes {
-    constructor() ERC20("MyToken", "MTK") ERC20Permit("MyToken") {}
-
-    // Overrides IERC6372 functions to make the token & governor timestamp-based
-
-    function clock() public view override returns (uint48) {
-        return uint48(block.timestamp);
-    }
-
-    function CLOCK_MODE() public pure override returns (string memory) {
-        return "mode=timestamp";
-    }
-
-    // The functions below are overrides required by Solidity.
-
-    function _afterTokenTransfer(address from, address to, uint256 amount)
-        internal
-        override(ERC20, ERC20Votes)
-    {
-        super._afterTokenTransfer(from, to, amount);
-    }
-
-    function _mint(address to, uint256 amount)
-        internal
-        override(ERC20, ERC20Votes)
-    {
-        super._mint(to, amount);
-    }
-
-    function _burn(address account, uint256 amount)
-        internal
-        override(ERC20, ERC20Votes)
-    {
-        super._burn(account, amount);
-    }
-}
+include::api:example$governance/MyTokenTimestampBased.sol[]
 ```
 
 === Governor

+ 88 - 0
docs/modules/api/examples/governance/MyGovernor.sol

@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.2;
+
+import "@openzeppelin/contracts/../governance/Governor.sol";
+import "@openzeppelin/contracts/../governance/compatibility/GovernorCompatibilityBravo.sol";
+import "@openzeppelin/contracts/../governance/extensions/GovernorVotes.sol";
+import "@openzeppelin/contracts/../governance/extensions/GovernorVotesQuorumFraction.sol";
+import "@openzeppelin/contracts/../governance/extensions/GovernorTimelockControl.sol";
+
+contract MyGovernor is
+    Governor,
+    GovernorCompatibilityBravo,
+    GovernorVotes,
+    GovernorVotesQuorumFraction,
+    GovernorTimelockControl
+{
+    constructor(
+        IVotes _token,
+        TimelockController _timelock
+    ) Governor("MyGovernor") GovernorVotes(_token) GovernorVotesQuorumFraction(4) GovernorTimelockControl(_timelock) {}
+
+    function votingDelay() public pure override returns (uint256) {
+        return 7200; // 1 day
+    }
+
+    function votingPeriod() public pure override returns (uint256) {
+        return 50400; // 1 week
+    }
+
+    function proposalThreshold() public pure override returns (uint256) {
+        return 0;
+    }
+
+    // The functions below are overrides required by Solidity.
+
+    function state(
+        uint256 proposalId
+    ) public view override(Governor, IGovernor, GovernorTimelockControl) returns (ProposalState) {
+        return super.state(proposalId);
+    }
+
+    function propose(
+        address[] memory targets,
+        uint256[] memory values,
+        bytes[] memory calldatas,
+        string memory description
+    ) public override(Governor, GovernorCompatibilityBravo, IGovernor) returns (uint256) {
+        return super.propose(targets, values, calldatas, description);
+    }
+
+    function cancel(
+        address[] memory targets,
+        uint256[] memory values,
+        bytes[] memory calldatas,
+        bytes32 descriptionHash
+    ) public override(Governor, GovernorCompatibilityBravo, IGovernor) returns (uint256) {
+        return super.cancel(targets, values, calldatas, descriptionHash);
+    }
+
+    function _execute(
+        uint256 proposalId,
+        address[] memory targets,
+        uint256[] memory values,
+        bytes[] memory calldatas,
+        bytes32 descriptionHash
+    ) internal override(Governor, GovernorTimelockControl) {
+        super._execute(proposalId, targets, values, calldatas, descriptionHash);
+    }
+
+    function _cancel(
+        address[] memory targets,
+        uint256[] memory values,
+        bytes[] memory calldatas,
+        bytes32 descriptionHash
+    ) internal override(Governor, GovernorTimelockControl) returns (uint256) {
+        return super._cancel(targets, values, calldatas, descriptionHash);
+    }
+
+    function _executor() internal view override(Governor, GovernorTimelockControl) returns (address) {
+        return super._executor();
+    }
+
+    function supportsInterface(
+        bytes4 interfaceId
+    ) public view override(Governor, IERC165, GovernorTimelockControl) returns (bool) {
+        return super.supportsInterface(interfaceId);
+    }
+}

+ 24 - 0
docs/modules/api/examples/governance/MyToken.sol

@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.2;
+
+import "@openzeppelin/contracts/../token/ERC20/ERC20.sol";
+import "@openzeppelin/contracts/../token/ERC20/extensions/ERC20Permit.sol";
+import "@openzeppelin/contracts/../token/ERC20/extensions/ERC20Votes.sol";
+
+contract MyToken is ERC20, ERC20Permit, ERC20Votes {
+    constructor() ERC20("MyToken", "MTK") ERC20Permit("MyToken") {}
+
+    // The functions below are overrides required by Solidity.
+
+    function _afterTokenTransfer(address from, address to, uint256 amount) internal override(ERC20, ERC20Votes) {
+        super._afterTokenTransfer(from, to, amount);
+    }
+
+    function _mint(address to, uint256 amount) internal override(ERC20, ERC20Votes) {
+        super._mint(to, amount);
+    }
+
+    function _burn(address account, uint256 amount) internal override(ERC20, ERC20Votes) {
+        super._burn(account, amount);
+    }
+}

+ 35 - 0
docs/modules/api/examples/governance/MyTokenTimestampBased.sol

@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.2;
+
+import "@openzeppelin/contracts/../token/ERC20/ERC20.sol";
+import "@openzeppelin/contracts/../token/ERC20/extensions/ERC20Permit.sol";
+import "@openzeppelin/contracts/../token/ERC20/extensions/ERC20Votes.sol";
+
+contract MyTokenTimestampBased is ERC20, ERC20Permit, ERC20Votes {
+    constructor() ERC20("MyTokenTimestampBased", "MTK") ERC20Permit("MyTokenTimestampBased") {}
+
+    // Overrides IERC6372 functions to make the token & governor timestamp-based
+
+    function clock() public view override returns (uint48) {
+        return uint48(block.timestamp);
+    }
+
+    // solhint-disable-next-line func-name-mixedcase
+    function CLOCK_MODE() public pure override returns (string memory) {
+        return "mode=timestamp";
+    }
+
+    // The functions below are overrides required by Solidity.
+
+    function _afterTokenTransfer(address from, address to, uint256 amount) internal override(ERC20, ERC20Votes) {
+        super._afterTokenTransfer(from, to, amount);
+    }
+
+    function _mint(address to, uint256 amount) internal override(ERC20, ERC20Votes) {
+        super._mint(to, amount);
+    }
+
+    function _burn(address account, uint256 amount) internal override(ERC20, ERC20Votes) {
+        super._burn(account, amount);
+    }
+}

+ 31 - 0
docs/modules/api/examples/governance/MyTokenWrapped.sol

@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.2;
+
+import "@openzeppelin/contracts/../token/ERC20/ERC20.sol";
+import "@openzeppelin/contracts/../token/ERC20/extensions/ERC20Permit.sol";
+import "@openzeppelin/contracts/../token/ERC20/extensions/ERC20Votes.sol";
+import "@openzeppelin/contracts/../token/ERC20/extensions/ERC20Wrapper.sol";
+
+contract MyTokenWrapped is ERC20, ERC20Permit, ERC20Votes, ERC20Wrapper {
+    constructor(
+        IERC20 wrappedToken
+    ) ERC20("MyTokenWrapped", "MTK") ERC20Permit("MyTokenWrapped") ERC20Wrapper(wrappedToken) {}
+
+    // The functions below are overrides required by Solidity.
+
+    function decimals() public pure override(ERC20, ERC20Wrapper) returns (uint8) {
+        return 18;
+    }
+
+    function _afterTokenTransfer(address from, address to, uint256 amount) internal override(ERC20, ERC20Votes) {
+        super._afterTokenTransfer(from, to, amount);
+    }
+
+    function _mint(address to, uint256 amount) internal override(ERC20, ERC20Votes) {
+        super._mint(to, amount);
+    }
+
+    function _burn(address account, uint256 amount) internal override(ERC20, ERC20Votes) {
+        super._burn(account, amount);
+    }
+}

+ 2 - 2
package-lock.json

@@ -1,12 +1,12 @@
 {
   "name": "openzeppelin-solidity",
-  "version": "4.8.2",
+  "version": "4.9.0",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
       "name": "openzeppelin-solidity",
-      "version": "4.8.2",
+      "version": "4.9.0",
       "license": "MIT",
       "bin": {
         "openzeppelin-contracts-migrate-imports": "scripts/migrate-imports.js"

+ 8 - 5
scripts/prepare-docs.sh

@@ -1,6 +1,7 @@
 #!/usr/bin/env bash
 
 set -euo pipefail
+shopt -s globstar
 
 OUTDIR="$(node -p 'require("./docs/config.js").outputDir')"
 
@@ -13,11 +14,13 @@ rm -rf "$OUTDIR"
 hardhat docgen
 
 # copy examples and adjust imports
-examples_dir="docs/modules/api/examples"
-mkdir -p "$examples_dir"
-for f in contracts/mocks/docs/*.sol; do
-  name="$(basename "$f")"
-  sed -e '/^import/s|\.\./\.\./|@openzeppelin/contracts/|' "$f" > "docs/modules/api/examples/$name"
+examples_source_dir="contracts/mocks/docs"
+examples_target_dir="docs/modules/api/examples"
+
+for f in "$examples_source_dir"/**/*.sol; do
+  name="${f/#"$examples_source_dir/"/}"
+  mkdir -p "$examples_target_dir/$(dirname "$name")"
+  sed -e '/^import/s|\.\./\.\./|@openzeppelin/contracts/|' "$f" > "$examples_target_dir/$name"
 done
 
 node scripts/gen-nav.js "$OUTDIR" > "$OUTDIR/../nav.adoc"