Posted By : Mudit
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
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
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);
}
}
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
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.
November 18, 2024 at 03:35 pm
Your comment is awaiting moderation.