Create Core Contract

Installation

Install Forge from Foundry and add the modular contract framework:

forge init
forge install thirdweb-dev/modular-contracts --no-commit

Add the Thirdweb modular contracts to foundry.toml under remappings:

remappings = ['@thirdweb-dev=lib/modular-contracts/']

Setup Core Contract

  • Create a Core Contract

    Create a new file in the src folder called CounterCore.sol, and inherit the Core contract.

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.20;
    import {Core} from "@thirdweb-dev/src/Core.sol";
    import {BeforeIncrementCallback} from "./interface/BeforeIncrementCallback.sol";
    contract CounterCore is Core {
    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.

  • Set Get Supported Callback Function

    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.

    // 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)
    {}
    }
  • Set Up Increment Function

    Define a function to increment a counter.

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.20;
    import {Core} from "@thirdweb-dev/src/Core.sol";
    contract CounterCore is Core {
    uint256 public count;
    constructor(address owner) {
    _initializeOwner(owner);
    }
    function getSupportedCallbackFunctions()
    public
    pure
    override
    returns (SupportedCallbackFunction[] memory supportedCallbackFunctions)
    {}
    // 👇👇👇👇👇👇👇👇👇
    function increment() public {
    count += 1;
    }
    }
  • Add a Callback Function

    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.

    // 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)
    {}
    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));
    }
    }
  • Implement Supported Functions

    Implement the getSupportedCallbackFunctions and supportsInterface functions to expose which callback functions and interfaces this core contract supports.

    // 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);
    }
    }

This guide will help you create a core contract that can increment a counter with optional callback functions for additional modular functionality.