From 17127d77632c5a59dff92900a89d2bb6b4d9bf74 Mon Sep 17 00:00:00 2001 From: Vasiliy Gualoto Date: Fri, 18 Oct 2024 18:14:38 -0500 Subject: [PATCH] =?UTF-8?q?Docs(update):=20Correcting=20the=20Modular=20co?= =?UTF-8?q?ntract=20getting=20started=20tutoria=E2=80=A6=20(#5074)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../get-started/create-core-contract/page.mdx | 279 +++++--- .../create-module-contract/page.mdx | 668 ++++++++---------- 2 files changed, 477 insertions(+), 470 deletions(-) diff --git a/apps/portal/src/app/contracts/modular-contracts/get-started/create-core-contract/page.mdx b/apps/portal/src/app/contracts/modular-contracts/get-started/create-core-contract/page.mdx index 82afb532d30..be2630daa44 100644 --- a/apps/portal/src/app/contracts/modular-contracts/get-started/create-core-contract/page.mdx +++ b/apps/portal/src/app/contracts/modular-contracts/get-started/create-core-contract/page.mdx @@ -18,8 +18,13 @@ Install Forge from Foundry and add the modular contract framework: ```bash forge init -forge install https://github.com/thirdweb-dev/modular-contracts.git -forge remappings > remappings.txt +forge install thirdweb-dev/modular-contracts --no-commit +``` + +Add the Thirdweb modular contracts to `foundry.toml` under `remappings`: + +```toml +remappings = ['@thirdweb-dev=lib/modular-contracts/'] ``` ### Setup Core Contract @@ -29,152 +34,196 @@ forge remappings > remappings.txt Create a new file in the `src` folder called `CounterCore.sol`, and inherit the `Core` contract. - ```solidity - // SPDX-License-Identifier: UNLICENSED - pragma solidity ^0.8.20; +```solidity + // SPDX-License-Identifier: MIT + pragma solidity ^0.8.20; - import {Core} from "modular-contracts/src/Core.sol"; + import {Core} from "@thirdweb-dev/src/Core.sol"; + import {BeforeIncrementCallback} from "./interface/BeforeIncrementCallback.sol"; - contract CounterCore is Core { + contract CounterCore is Core { + constructor(address owner) { + _initializeOwner(owner); + } + } - constructor(address owner) { - _initializeOwner(owner); - } +``` + +> **Note** +> The `Core` contract is the base contract that needs to be inherited for this contract to be recognized as a core contract. + + - } - ``` + +Implement the `getSupportedCallbackFunctions` function. The Core contract is abstract because this function is not implemented. To avoid compilation errors, declare the function with an empty body for now. - > **Note** - > The `Core` contract is the base contract that needs to be inherited for this contract to be recognized as a core contract. + ```solidity + // SPDX-License-Identifier: MIT + pragma solidity ^0.8.20; + + import {Core} from "@thirdweb-dev/src/Core.sol"; + + contract CounterCore is Core { + constructor(address owner) { + _initializeOwner(owner); + } + + function getSupportedCallbackFunctions() + public + pure + override + returns (SupportedCallbackFunction[] memory supportedCallbackFunctions) + {} + } + ``` Define a function to increment a counter. - ```solidity - // SPDX-License-Identifier: UNLICENSED - pragma solidity ^0.8.20; +```solidity + // SPDX-License-Identifier: MIT + pragma solidity ^0.8.20; - import {Core} from "modular-contracts/src/Core.sol"; + import {Core} from "@thirdweb-dev/src/Core.sol"; - contract CounterCore is Core { - uint256 public count; + contract CounterCore is Core { + uint256 public count; - constructor(address owner) { - _initializeOwner(owner); - } + constructor(address owner) { + _initializeOwner(owner); + } + + function getSupportedCallbackFunctions() + public + pure + override + returns (SupportedCallbackFunction[] memory supportedCallbackFunctions) + {} - function increment() public { - count += 1; - } - } - ``` + // 👇👇👇👇👇👇👇👇👇 + function increment() public { + count += 1; + } + } +``` - Introduce the `_beforeIncrement` function to use the `beforeIncrement` callback from a module. + Introduce the `_beforeIncrement` function to use the `beforeIncrement` callback from a module to achieve this, we'll introduce the interface `BeforeIncrementCallback` - > **Note** - > Callback functions are hook-like functionalities that can be used before or after the main functionality of a core contract. - > In this example, the `beforeIncrement` callback is executed before the main increment functionality. +> **Note** +> Callback functions are hook-like functionalities that can be used before or after the main functionality of a core contract. +> In this example, the `beforeIncrement` callback is executed before the main increment functionality. - ```solidity - // SPDX-License-Identifier: UNLICENSED - pragma solidity ^0.8.20; +```solidity + // SPDX-License-Identifier: MIT + pragma solidity ^0.8.20; - import {Core} from "modular-contracts/src/Core.sol"; + import {Core} from "@thirdweb-dev/src/Core.sol"; - interface BeforeIncrementCallback { - function beforeIncrement(uint256 count) external returns (uint256); - } + // 👇👇👇👇👇👇👇👇👇 - contract CounterCore is Core { - uint256 public count; + interface BeforeIncrementCallback { + function beforeIncrement(uint256 count) external returns (uint256); + } - constructor(address owner) { - _initializeOwner(owner); - } + contract CounterCore is Core { + uint256 public count; - function increment() public { - uint256 newCount = _beforeIncrement(count); - count = newCount; - } - - function _beforeIncrement( - uint256 count - ) internal returns (uint256 newCount) { - (, bytes memory returndata) = _executeCallbackFunction( - BeforeIncrementCallback.beforeIncrement.selector, - abi.encodeCall(BeforeIncrementCallback.beforeIncrement, (count)) - ); - newCount = abi.decode(returndata, (uint256)); - } - } - ``` + constructor(address owner) { + _initializeOwner(owner); + } - + function getSupportedCallbackFunctions() + public + pure + override + returns (SupportedCallbackFunction[] memory supportedCallbackFunctions) + {} - - Implement the `getSupportedCallbackFunctions` and `supportsInterface` functions to expose which callback functions and interfaces this core contract supports. + function increment() public { + count += 1; + } - ```solidity - // SPDX-License-Identifier: UNLICENSED - pragma solidity ^0.8.20; + // 👇👇👇👇👇👇👇👇👇 - import {Core} from "modular-contracts/src/Core.sol"; + function _beforeIncrement( + uint256 _count + ) internal returns (uint256 newCount) { + (, bytes memory returnData) = _executeCallbackFunction( + BeforeIncrementCallback.beforeIncrement.selector, + abi.encodeCall(BeforeIncrementCallback.beforeIncrement, (_count)) + ); - interface BeforeIncrementCallback { - function beforeIncrement(uint256 count) external returns (uint256); - } + newCount = abi.decode(returnData, (uint256)); + } + } - contract CounterCore is Core { - uint256 public count; +``` - constructor(address owner) { - _initializeOwner(owner); - } + - function increment() public { - uint256 newCount = _beforeIncrement(count); - count = newCount; - } - - function getSupportedCallbackFunctions() - public - pure - override - returns (SupportedCallbackFunction[] memory supportedCallbackFunctions) - { - supportedCallbackFunctions = new SupportedCallbackFunction ; - supportedCallbackFunctions[0] = SupportedCallbackFunction({ - selector: BeforeIncrementCallback.beforeIncrement.selector, - mode: CallbackMode.REQUIRED - }); - } - - function supportsInterface(bytes4 interfaceId) - public - view - override - returns (bool) - { - return - interfaceId == 0x00000001 || super.supportsInterface(interfaceId); - } - - function _beforeIncrement( - uint256 count - ) internal returns (uint256 newCount) { - (bool success, bytes memory returndata) = _executeCallbackFunction( - BeforeIncrementCallback.beforeIncrement.selector, - abi.encodeCall(BeforeIncrementCallback.beforeIncrement, (count)) - ); - newCount = abi.decode(returndata, (uint256)); - } - } - ``` + + Implement the `getSupportedCallbackFunctions` and `supportsInterface` functions to expose which callback functions and interfaces this core contract supports. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {Core} from "@thirdweb-dev/src/Core.sol"; + +interface BeforeIncrementCallback { + function beforeIncrement(uint256 count) external returns (uint256); +} + +contract CounterCore is Core { + uint256 public count; + + constructor(address owner) { + _initializeOwner(owner); + } + + // 👇👇👇👇👇👇👇👇👇 + function getSupportedCallbackFunctions() + public + pure + override + returns (SupportedCallbackFunction[] memory supportedCallbackFunctions) + { + supportedCallbackFunctions = new SupportedCallbackFunction[](1); + supportedCallbackFunctions[0] = SupportedCallbackFunction({ + selector: BeforeIncrementCallback.beforeIncrement.selector, + mode: CallbackMode.OPTIONAL + }); + } + + function increment() public { + count += 1; + } + + function _beforeIncrement( + uint256 _count + ) internal returns (uint256 newCount) { + (, bytes memory returnData) = _executeCallbackFunction( + BeforeIncrementCallback.beforeIncrement.selector, + abi.encodeCall(BeforeIncrementCallback.beforeIncrement, (_count)) + ); + + newCount = abi.decode(returnData, (uint256)); + } + + // 👇👇👇👇👇👇👇👇👇 + function supportsInterface( + bytes4 interfaceId + ) public view override returns (bool) { + return interfaceId == 0x00000001 || super.supportsInterface(interfaceId); + } +} + +``` @@ -183,5 +232,3 @@ forge remappings > remappings.txt --- This guide will help you create a core contract that can increment a counter with optional callback functions for additional modular functionality. - - diff --git a/apps/portal/src/app/contracts/modular-contracts/get-started/create-module-contract/page.mdx b/apps/portal/src/app/contracts/modular-contracts/get-started/create-module-contract/page.mdx index acb3b49497e..ffe3de55bbb 100644 --- a/apps/portal/src/app/contracts/modular-contracts/get-started/create-module-contract/page.mdx +++ b/apps/portal/src/app/contracts/modular-contracts/get-started/create-module-contract/page.mdx @@ -21,11 +21,17 @@ export const metadata = createMetadata({ For assistance, refer to the [Foundry installation guide](https://book.getfoundry.sh/getting-started/installation). - ```bash - forge init - forge install https://github.com/thirdweb-dev/modular-contracts.git - forge remappings > remappings.txt - ``` +```bash +forge init +forge install thirdweb-dev/modular-contracts --no-commit +``` + +Add the Thirdweb modular contracts to `foundry.toml` under `remappings`: + +```toml +remappings = ['@thirdweb-dev=lib/modular-contracts/'] +``` + ### Create Module @@ -33,388 +39,342 @@ export const metadata = createMetadata({ Create a new file called `CounterModule.sol` in the `src` folder and start with the following code: - ```solidity - // SPDX-License-Identifier: UNLICENSED - pragma solidity ^0.8.20; +```solidity +//SPDX-License-Identifier: MIT + +pragma solidity ^0.8.20; + +import {Module} from "@thirdweb-dev/src/Module.sol"; - import {Module} from "modular-contracts/src/Module.sol"; +contract CounterModule is Module { + function getModuleConfig() + public + pure + override + returns (ModuleConfig memory config) + {} +} +``` - contract counterModule is Module {} - ``` +> **Note** +> The `Module` contract is the base contract that needs to be inherited for this contract to be recognized as a Module Contract, and we need to implement the `getModuleConfig` function to prevent the contract to be marked as abstract. - > **Note** - > The `Module` contract is the base contract that needs to be inherited for this contract to be recognized as a Module Contract. Create a library called `CounterStorage` responsible for holding the state of the Module Contract: - ```solidity - // SPDX-License-Identifier: UNLICENSED - pragma solidity ^0.8.20; +```solidity +//SPDX-License-Identifier: MIT - import {Module} from "modular-contracts/src/Module.sol"; +pragma solidity ^0.8.20; - library CounterStorage { - /// @custom:storage-location erc7201:token.minting.counter - bytes32 public constant COUNTER_STORAGE_POSITION = - keccak256(abi.encode(uint256(keccak256("counter")) - 1)) & - ~bytes32(uint256(0xff)); +import {Module} from "@thirdweb-dev/src/Module.sol"; - struct Data { - uint256 step; - } +contract CounterModule is Module { + function getModuleConfig() + public + pure + override + returns (ModuleConfig memory config) + {} +} - function data() internal pure returns (Data storage data_) { - bytes32 position = COUNTER_STORAGE_POSITION; - assembly { - data_.slot := position; - } - } - } + // 👇👇👇👇👇👇👇👇👇 +library CounterStorage { + /// @custom:storage-location erc7201:token.minting.counter + bytes32 public constant COUNTER_STORAGE_POSITION = + keccak256(abi.encode(uint256(keccak256("counter")) - 1)) & + ~bytes32(uint256(0xff)); - contract counterModule is Module {} - ``` + struct Data { + uint256 step; + } + + function data() internal pure returns (Data storage data_) { + bytes32 position = COUNTER_STORAGE_POSITION; + assembly { + data_.slot := position + } + } +} + +``` + +> **Note** +> The library `CounterStorage` uses the ERC-7201: Namespace storage layout to store the data. Learn more about [ERC-7201](https://eips.ethereum.org/EIPS/eip-7201). - > **Note** - > The library `CounterStorage` uses the ERC-7201: Namespace storage layout to store the data. Learn more about [ERC-7201](https://eips.ethereum.org/EIPS/eip-7201). Set up the function `_counterStorage` to access the storage from the `CounterStorage` library: - ```solidity - // SPDX-License-Identifier: UNLICENSED - pragma solidity ^0.8.20; - - import {Module} from "modular-contracts/src/Module.sol"; - - library CounterStorage { - /// @custom:storage-location erc7201:token.minting.counter - bytes32 public constant COUNTER_STORAGE_POSITION = - keccak256(abi.encode(uint256(keccak256("counter")) - 1)) & - ~bytes32(uint256(0xff)); - - struct Data { - uint256 step; - } - - function data() internal pure returns (Data storage data_) { - bytes32 position = COUNTER_STORAGE_POSITION; - assembly { - data_.slot := position; - } - } - } - - contract counterModule is Module { - /*////////////////////////////////////////////////////////////// - Internal Functions - //////////////////////////////////////////////////////////////*/ - - function _counterStorage() - internal - pure - returns (CounterStorage.Data storage) - { - return CounterStorage.data(); - } - } - ``` +```solidity +//SPDX-License-Identifier: MIT + +pragma solidity ^0.8.20; + +import {Module} from "@thirdweb-dev/src/Module.sol"; + +contract CounterModule is Module { + function getModuleConfig() + public + pure + override + returns (ModuleConfig memory config) + {} + + // 👇👇👇👇👇👇👇👇👇 + + function _counterStorage() + internal + pure + returns (CounterStorage.Data storage) + { + return CounterStorage.data(); + } +} + +library CounterStorage { + /// @custom:storage-location erc7201:token.minting.counter + bytes32 public constant COUNTER_STORAGE_POSITION = + keccak256(abi.encode(uint256(keccak256("counter")) - 1)) & + ~bytes32(uint256(0xff)); + + struct Data { + uint256 step; + } + + fun2ction data() internal pure returns (Data storage data_) { + bytes32 position = COUNTER_STORAGE_POSITION; + assembly { + data_.slot := position + } + } +} + +``` + Set up fallback functions that act as the setters and getters for `step`: - ```solidity - // SPDX-License-Identifier: UNLICENSED - pragma solidity ^0.8.20; - - import {Module} from "modular-contracts/src/Module.sol"; - - library CounterStorage { - /// @custom:storage-location erc7201:token.minting.counter - bytes32 public constant COUNTER_STORAGE_POSITION = - keccak256(abi.encode(uint256(keccak256("counter")) - 1)) & - ~bytes32(uint256(0xff)); - - struct Data { - uint256 step; - } - - function data() internal pure returns (Data storage data_) { - bytes32 position = COUNTER_STORAGE_POSITION; - assembly { - data_.slot := position; - } - } - } - - contract counterModule is Module { - /*////////////////////////////////////////////////////////////// - Callback & Fallback Functions - //////////////////////////////////////////////////////////////*/ - - function getStep() external view returns (uint256) { - return _counterStorage().step; - } - - function setStep(uint256 step) external { - _counterStorage().step = step; - } - - /*////////////////////////////////////////////////////////////// - Internal Functions - //////////////////////////////////////////////////////////////*/ - - function _counterStorage() - internal - pure - returns (CounterStorage.Data storage) - { - return CounterStorage.data(); - } - } - ``` - - > **Note** - > Fallback functions are extra functionalities that a core contract can use via the Solidity `fallback` function. +```solidity +//SPDX-License-Identifier: MIT + +pragma solidity ^0.8.20; + +import {Module} from "@thirdweb-dev/src/Module.sol"; + +contract CounterModule is Module { + function getModuleConfig() + public + pure + override + returns (ModuleConfig memory config) + {} + + function _counterStorage() + internal + pure + returns (CounterStorage.Data storage) + { + return CounterStorage.data(); + } + + // 👇👇👇👇👇👇👇👇👇 + // Fallback Functions + function getStep() external view returns (uint256) { + return _counterStorage().step; + } + + function setStep(uint256 _step) external { + _counterStorage().step = _step; + } +} + +library CounterStorage { + /// @custom:storage-location erc7201:token.minting.counter + bytes32 public constant COUNTER_STORAGE_POSITION = + keccak256(abi.encode(uint256(keccak256("counter")) - 1)) & + ~bytes32(uint256(0xff)); + + struct Data { + uint256 step; + } + + function data() internal pure returns (Data storage data_) { + bytes32 position = COUNTER_STORAGE_POSITION; + assembly { + data_.slot := position + } + } +} + +``` + +> **Note** +> Fallback functions are extra functionalities that a core contract can use via the Solidity `fallback` function. + Set up a callback function `beforeIncrement` that increases the given count by `step`: - ```solidity - // SPDX-License-Identifier: UNLICENSED - pragma solidity ^0.8.20; - - import {Module} from "modular-contracts/src/Module.sol"; - - library CounterStorage { - /// @custom:storage-location erc7201:token.minting.counter - bytes32 public constant COUNTER_STORAGE_POSITION = - keccak256(abi.encode(uint256(keccak256("counter")) - 1)) & - ~bytes32(uint256(0xff)); - - struct Data { - uint256 step; - } - - function data() internal pure returns (Data storage data_) { - bytes32 position = COUNTER_STORAGE_POSITION; - assembly { - data_.slot := position; - } - } - } - - contract counterModule is Module { - /*////////////////////////////////////////////////////////////// - Callback & Fallback Functions - //////////////////////////////////////////////////////////////*/ - - function beforeIncrement(uint256 count) external view returns (uint256) { - return count + _counterStorage().step; - } - - function getStep() external view returns (uint256) { - return _counterStorage().step; - } - - function setStep(uint256 step) external { - _counterStorage().step = step; - } - - /*////////////////////////////////////////////////////////////// - Internal Functions - //////////////////////////////////////////////////////////////*/ - - function _counterStorage() - internal - pure - returns (CounterStorage.Data storage) - { - return CounterStorage.data(); - } - } - ``` - - > **Note** - > Callback functions are hook-like functionalities that can be used before or after the main functionality of a core contract. In this snippet, the `beforeIncrement` callback is used before the main increment functionality. - +```solidity +//SPDX-License-Identifier: MIT + +pragma solidity ^0.8.20; + +import {Module} from "@thirdweb-dev/src/Module.sol"; + +contract CounterModule is Module { + function getModuleConfig() + public + pure + override + returns (ModuleConfig memory config) + {} + + function _counterStorage() + internal + pure + returns (CounterStorage.Data storage) + { + return CounterStorage.data(); + } + + // Fallback Functions + function getStep() external view returns (uint256) { + return _counterStorage().step; + } + + function setStep(uint256 _step) external { + _counterStorage().step = _step; + } + + + // 👇👇👇👇👇👇👇👇👇 + function beforeIncrement(uint256 count) external view returns (uint256) { + return count + _counterStorage().step; + } +} + +library CounterStorage { + /// @custom:storage-location erc7201:token.minting.counter + bytes32 public constant COUNTER_STORAGE_POSITION = + keccak256(abi.encode(uint256(keccak256("counter")) - 1)) & + ~bytes32(uint256(0xff)); + + struct Data { + uint256 step; + } + + function data() internal pure returns (Data storage data_) { + bytes32 position = COUNTER_STORAGE_POSITION; + assembly { + data_.slot := position + } + } +} + +``` + +> **Note** +> Callback functions are hook-like functionalities that can be used before or after the main functionality of a core contract. In this snippet, the `beforeIncrement` callback is used before the main increment functionality. - - Add the `onInstall` and `onUninstall` functions along with helper functions `encodeBytesOnInstall` and `encodeBytesOnUninstall`: - - ```solidity - // SPDX-License-Identifier: UNLICENSED - pragma solidity ^0.8.20; - - import {Module} from "modular-contracts/src/Module.sol"; - - library CounterStorage { - /// @custom:storage-location erc7201:token.minting.counter - bytes32 public constant COUNTER_STORAGE_POSITION = - keccak256(abi.encode(uint256(keccak256("counter")) - 1)) & - ~bytes32(uint256(0xff)); - - struct Data { - uint256 step; - } - - function data() internal pure returns (Data storage data_) { - bytes32 position = COUNTER_STORAGE_POSITION; - assembly { - data_.slot := position; - } - } - } - - contract counterModule is Module { - /*////////////////////////////////////////////////////////////// - Install / Uninstall - //////////////////////////////////////////////////////////////*/ - - function onInstall(bytes calldata data) external { - uint256 step = abi.decode(data, (uint256)); - _counterStorage().step = step; - } - - function onUninstall(bytes calldata data) external {} - - function encodeBytesOnInstall( - uint256 step - ) external pure returns (bytes memory) { - return abi.encode(step); - } - - function encodeBytesOnUninstall() external pure returns (bytes memory) { - return ""; - } - - /*////////////////////////////////////////////////////////////// - Callback & Fallback Functions - //////////////////////////////////////////////////////////////*/ - - function beforeIncrement(uint256 count) external view returns (uint256) { - return count + _counterStorage().step; - } - - function getStep() external view returns (uint256) { - return _counterStorage().step; - } - - function setStep(uint256 step) external { - _counterStorage().step = step; - } - - /*////////////////////////////////////////////////////////////// - Internal Functions - //////////////////////////////////////////////////////////////*/ - - function _counterStorage() - internal - pure - returns (CounterStorage.Data storage) - { - return CounterStorage.data(); - } - } - ``` - Lastly, set up the `getModuleConfig` function which is responsible for communicating to the core contract: - - ```solidity - // SPDX-License-Identifier: UNLICENSED - pragma solidity ^0.8.20; - - import {Module} from "modular-contracts/src/Module.sol"; - - library CounterStorage { - /// @custom:storage-location erc7201:token.minting.counter - bytes32 public constant COUNTER_STORAGE_POSITION = - keccak256(abi.encode(uint256(keccak256("counter")) - 1)) & - ~bytes32(uint256( - -0xff)); - - struct Data { - uint256 step; - } - - function data() internal pure returns (Data storage data_) { - bytes32 position = COUNTER_STORAGE_POSITION; - assembly { - data_.slot := position; - } - } - } - - contract counterModule is Module { - /*////////////////////////////////////////////////////////////// - Module Config - //////////////////////////////////////////////////////////////*/ - - function getModuleConfig() - public - pure - override - returns (ModuleConfig memory config) - { - config.callbackFunctions = new CallbackFunction[](1); - config.fallbackFunctions = new FallbackFunction[](2); - - config.callbackFunctions[0] = CallbackFunction( - this.beforeIncrement.selector - ); - - config.fallbackFunctions[0] = FallbackFunction({ - selector: this.getStep.selector, - permissionBits: 0 - }); - - config.fallbackFunctions[1] = FallbackFunction({ - selector: this.setStep.selector, - permissionBits: Role._MANAGER_ROLE - }); - - config.requiredInterfaces = new bytes4 ; - config.requiredInterfaces[0] = 0x00000001; - - config.registerInstallationCallback = true; - } - - /*////////////////////////////////////////////////////////////// - Callback & Fallback Functions - //////////////////////////////////////////////////////////////*/ - - function beforeIncrement(uint256 count) external view returns (uint256) { - return count + _counterStorage().step; - } - - function getStep() external view returns (uint256) { - return _counterStorage().step; - } - - function setStep(uint256 step) external { - _counterStorage().step = step; - } - - /*////////////////////////////////////////////////////////////// - Internal Functions - //////////////////////////////////////////////////////////////*/ - - function _counterStorage() - internal - pure - returns (CounterStorage.Data storage) - { - return CounterStorage.data(); - } - } - ``` + Lastly, set up the `getModuleConfig` functionallity as this is the one which is responsible for communicating to the core contract: + +```solidity +//SPDX-License-Identifier: MIT + +pragma solidity ^0.8.20; + +import {Module} from "@thirdweb-dev/src/Module.sol"; +// 👇👇👇👇👇👇👇👇👇 +import {Role} from "@thirdweb-dev/src/Role.sol"; + +contract CounterModule is Module { + // 👇👇👇👇👇👇👇👇👇 + function getModuleConfig() + public + pure + override + returns (ModuleConfig memory config) + { + // Callback Function array of one element + config.callbackFunctions = new CallbackFunction[](1); + // Fallback Function array of two elements + config.fallbackFunctions = new FallbackFunction[](2); + + //adding the functions to the arrays + config.callbackFunctions[0] = CallbackFunction( + this.beforeIncrement.selector + ); + + config.fallbackFunctions[0] = FallbackFunction({ + selector: this.getStep.selector, + permissionBits: 0 + }); + + config.fallbackFunctions[1] = FallbackFunction({ + selector: this.setStep.selector, + permissionBits: Role._MANAGER_ROLE + }); + + // Required interfaces for the Module + config.requiredInterfaces = new bytes4[](1); + config.requiredInterfaces[0] = 0x00000001; + + // register the intallation callback + config.registerInstallationCallback = true; + } + + function _counterStorage() + internal + pure + returns (CounterStorage.Data storage) + { + return CounterStorage.data(); + } + + // Fallback Functions + function getStep() external view returns (uint256) { + return _counterStorage().step; + } + + function setStep(uint256 _step) external { + _counterStorage().step = _step; + } + + function beforeIncrement(uint256 count) external view returns (uint256) { + return count + _counterStorage().step; + } +} + +library CounterStorage { + /// @custom:storage-location erc7201:token.minting.counter + bytes32 public constant COUNTER_STORAGE_POSITION = + keccak256(abi.encode(uint256(keccak256("counter")) - 1)) & + ~bytes32(uint256(0xff)); + + struct Data { + uint256 step; + } + + function data() internal pure returns (Data storage data_) { + bytes32 position = COUNTER_STORAGE_POSITION; + assembly { + data_.slot := position + } + } +} + +```