Smart contracts arenβt just standalone programs β they can also talk to each other.
This ability to interact is one of the most powerful features of Solidity and forms the foundation for DeFi, DAOs, and modular dApps.
In todayβs challenge, weβll build two contracts: one that performs calculations, and another that uses it β demonstrating contract communication using interfaces and address casting.
π§ What Youβll Learn
By the end of this project, youβll understand:
- How one smart contract can call another contractβs function.
- Why interfaces make cross-contract calls secure and scalable.
- The role of events in tracking inter-contract activity.
- The importance of modularity and reusability in Solidity design.
βοΈ Project Overview
Weβll create two contracts:
- Calculator.sol β handles math operations like add, subtract, multiply, divide.
- MathManager.sol β interacts with the Calculator to perform these operations on demand.
This structure mimics how modern blockchain apps split logic into multiple smart contracts β just like microservices in traditional software systems.
π§© Architecture Diagram
ββββββββββββββββββββ
β MathManager β
β (Controller) β
β β
β Calls functions β
β from Calculator β
ββββββββ¬ββββββββββββ
β
βΌ
ββββββββββββββββββββ
β Calculator β
β (Utility Logic) β
β Performs math β
ββββββββββββββββββββ
πͺ How It Works
- Deploy the Calculator contract first.
- Copy its deployed address.
- Deploy the MathManager contract and pass the Calculator address in the constructor.
- Call any
performAddition()
,performSubtraction()
, etc., via MathManager β it delegates logic to Calculator.
π‘ Real-World Use Case
This pattern is everywhere in Web3:
- DeFi protocols (e.g., Aave, Uniswap) use modular contracts for lending, trading, and rewards.
- DAOs have governance, treasury, and voting modules that interact with each other.
- NFT marketplaces call external contracts for royalty distribution or metadata updates.
Inter-contract communication keeps dApps modular, secure, and upgradeable.
π§± Full Source Code
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
/// @title Calculator - Basic math operations contract
/// @author Saurav
/// @notice Provides reusable math functions for other contracts
contract Calculator {
/// @notice Adds two numbers
function add(uint256 a, uint256 b) external pure returns (uint256) {
return a + b;
}
/// @notice Subtracts two numbers
function subtract(uint256 a, uint256 b) external pure returns (uint256) {
require(a >= b, "Calculator: underflow");
return a - b;
}
/// @notice Multiplies two numbers
function multiply(uint256 a, uint256 b) external pure returns (uint256) {
return a * b;
}
/// @notice Divides two numbers
function divide(uint256 a, uint256 b) external pure returns (uint256) {
require(b != 0, "Calculator: division by zero");
return a / b;
}
}
/// @title ICalculator - Interface for Calculator contract
/// @notice Used for safe interaction from external contracts
interface ICalculator {
function add(uint256 a, uint256 b) external pure returns (uint256);
function subtract(uint256 a, uint256 b) external pure returns (uint256);
function multiply(uint256 a, uint256 b) external pure returns (uint256);
function divide(uint256 a, uint256 b) external pure returns (uint256);
}
/// @title MathManager - Demonstrates inter-contract communication using interfaces
/// @notice Interacts with the Calculator contract to perform math operations
contract MathManager {
/// @dev Address of deployed Calculator contract (immutable for gas optimization)
ICalculator public immutable calculator;
/// @notice Emitted whenever a calculation is performed
event CalculationPerformed(string operation, uint256 result);
/// @param _calculator Address of the deployed Calculator contract
constructor(address _calculator) {
require(_calculator != address(0), "Invalid calculator address");
calculator = ICalculator(_calculator);
}
/// @notice Performs addition through Calculator contract
function performAddition(uint256 a, uint256 b) external returns (uint256 result) {
result = calculator.add(a, b);
emit CalculationPerformed("Addition", result);
}
/// @notice Performs subtraction through Calculator contract
function performSubtraction(uint256 a, uint256 b) external returns (uint256 result) {
result = calculator.subtract(a, b);
emit CalculationPerformed("Subtraction", result);
}
/// @notice Performs multiplication through Calculator contract
function performMultiplication(uint256 a, uint256 b) external returns (uint256 result) {
result = calculator.multiply(a, b);
emit CalculationPerformed("Multiplication", result);
}
/// @notice Performs division through Calculator contract
function performDivision(uint256 a, uint256 b) external returns (uint256 result) {
result = calculator.divide(a, b);
emit CalculationPerformed("Division", result);
}
}
π§ Key Takeaways
- Always use interfaces for external contract calls.
- Use immutable variables for efficiency and security.
- Emit events to make your contract actions traceable.
- Keep contracts modular β one contract shouldnβt do everything.
π Conclusion
Contract-to-contract interaction is the heart of modular and scalable blockchain systems.
Understanding this concept prepares you for advanced topics like DeFi protocol design, upgradeable contracts, and cross-chain messaging.
Youβve now built your first multi-contract system β a huge milestone in your #30DaysOfSolidity journey! π
Top comments (0)