How to Write and Deploy Modular Smart Contracts

Posted By : Mudit

Oct 03, 2024

Modular contracts enable highly configurable and upgradeable smart contract development, combining ease of use with security. They consist of two main components:

 

Core Contracts: These form the foundation of the modular system, managing key functions, data storage, and logic. Core contracts include access control mechanisms and define interfaces for module interactions.

 

Module Contracts: These add or remove functionalities to/from core contracts dynamically, allowing for flexibility. Modules can be reused across multiple core contracts, enabling upgrades without redeploying the core contract.

 

How They Work: Modules provide additional functionality via callback and fallback functions that interact with core contracts. Fallback functions operate independently, while callback functions augment core contract logic, enhancing dApp functionality.

 

You may also like | How to Create Play-to-Earn Gaming Smart Contracts

 

Setup | Writing and Deploying Modular Smart Contracts

 

Install Forge from Foundry and add the modular contract framework:

 

forge init
forge install https://github.com/thirdweb-dev/modular-contracts.git
forge remappings > remappings.txt

 

Contract

 

The ERC20Core contract is a type of ERC20 token that combines features from both the ModularCore and the standard ERC20 contract. It names the token "Test Token" and uses "TEST" as its symbol, with the deployer being the owner of the contract. A significant feature is the required beforeMint callback, which allows certain actions to be taken before new tokens are created. The mint function lets users create tokens while ensuring the callback is executed first. The BeforeMintCallback interface makes it easier to add custom logic from other contracts. Overall, ERC20Core offers a flexible way to develop custom tokens while maintaining essential ERC20 functions.

 

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {ModularCore} from "lib/modular-contracts/src/ModularCore.sol";
import {ERC20} from "lib/solady/src/tokens/ERC20.sol";

contract ERC20Core is ModularCore, ERC20 {
    constructor() {
        _setOwner(msg.sender);
    }

    function name() public view override returns (string memory) {
        return "Test Token";
    }

    function symbol() public view override returns (string memory) {
        return "TEST";
    }

    function getSupportedCallbackFunctions()
        public
        pure
        virtual
        override
        returns (SupportedCallbackFunction[] memory supportedCallbacks)
    {
        supportedCallbacks = new SupportedCallbackFunction[](1);
        supportedCallbacks[0] = SupportedCallbackFunction(BeforeMintCallback.beforeMint.selector, CallbackMode.REQUIRED);
    }

    function mint(address to, uint256 amount) external payable {
        _executeCallbackFunction(
            BeforeMintCallback.beforeMint.selector, abi.encodeCall(BeforeMintCallback.beforeMint, (to, amount))
        );
        _mint(to, amount);
    }
}

interface BeforeMintCallback {
    function beforeMint(address to, uint256 amount) external payable;
}

 

Also, Read | ERC 4337 : Account Abstraction for Ethereum Smart Contract Wallets

 

The PricedMint contract is a modular extension designed for token minting, leveraging Ownable for ownership management and ModularExtension for added functionality. It uses the PricedMintStorage module to maintain a structured storage system for the token price. The owner can set the minting price through the setPricePerUnit method. Before minting, the beforeMint function verifies that the provided ether matches the expected price based on the token quantity. If correct, the ether is transferred to the contract owner. The getExtensionConfig function defines the contract's callback and fallback functions, facilitating integration with other modular components.

 

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {ModularExtension} from "lib/modular-contracts/src/ModularExtension.sol";
import {Ownable} from "lib/solady/src/auth/Ownable.sol";

library PricedMintStorage {
    bytes32 public constant PRICED_MINT_STORAGE_POSITION =
        keccak256(abi.encode(uint256(keccak256("priced.mint")) - 1)) & ~bytes32(uint256(0xff));

    struct Data {
        uint256 pricePerUnit;
    }

    function data() internal pure returns (Data storage data_) {
        bytes32 position = PRICED_MINT_STORAGE_POSITION;
        assembly {
            data_.slot := position
        }
    }
}

contract PricedMint is Ownable, ModularExtension {
    function setPricePerUnit(uint256 price) external onlyOwner {
        PricedMintStorage.data().pricePerUnit = price;
    }

    function beforeMint(address to, uint256 amount) external payable {
        uint256 pricePerUnit = PricedMintStorage.data().pricePerUnit;
        uint256 expectedPrice = (amount * pricePerUnit) / 1e18;
        require(msg.value == expectedPrice, "PricedMint: invalid price sent");
        (bool success,) = owner().call{value: msg.value}("");
        require(success, "ERC20Core: failed to send value");
    }

    function getExtensionConfig() external pure virtual override returns (ExtensionConfig memory config) {
        config.callbackFunctions = new CallbackFunction ;
        config.callbackFunctions[0] = CallbackFunction(this.beforeMint.selector);
        config.fallbackFunctions = new FallbackFunction ;
        config.fallbackFunctions[0] = FallbackFunction(this.setPricePerUnit.selector, 0);
    }
}

 

Deploy

 

To deploy the Modular Contract, first get your API Key from the Thirdweb Dashboard. Then run npx thirdweb publish -k "THIRDWEB_API_KEY", replacing "THIRDWEB_API_KEY" with your key. Select "CounterModule," scroll down, click "Next," choose the "Sepolia" network, and click "Publish Contract."

For the Core Contract, run npx thirdweb deploy -k "THIRDWEB_API_KEY" in your terminal, replacing "THIRDWEB_API_KEY" with your key. Select "CounterCore," enter the contract owner's address, and click "Deploy Now." Choose the "Sepolia" chain and click "Deploy Now" again to start the deployment.

 

Also, Explore | How to Create a Smart Contract for Lottery System

 

Conclusion

 

Modular contracts represent a design approach in smart contract development that prioritizes flexibility, reusability, and separation of concerns. By breaking down complex functionalities into smaller, interchangeable modules, developers can create more maintainable code and implement updates more easily without losing existing state. Commonly utilized in token standards and decentralized finance (DeFi), modular contracts enhance the creation of decentralized applications (dApps) and promote interoperability, thereby fostering innovation within the blockchain ecosystem. If you are looking for enterprise-grade smart contract development services, connect with our skilled Solidity developers to get started. 

Leave a

Comment

Name is required

Invalid Name

Comment is required

Recaptcha is required.

blog-detail

October 21, 2024 at 12:29 pm

Your comment is awaiting moderation.

By using this site, you allow our use of cookies. For more information on the cookies we use and how to delete or block them, please read our cookie notice.

Chat with Us
Telegram Button
Youtube Button
Contact Us

Oodles | Blockchain Development Company

Name is required

Please enter a valid Name

Please enter a valid Phone Number

Please remove URL from text