solidity logo

Question: What are the mathematical operators and functions in Solidity? Please list them and explain one use case of a mathematical operator.

In Solidity, mathematical operators include the basic ones such as addition (+), subtraction (-), multiplication (*), division (/), and modulo (%). In addition, Solidity also supports exponentiation (**) and bitwise operators (&, |, ^, ~, <<, >>).


Example: Division Operator (/)

The division operator is used to perform division. For example, in a reward distribution scenario, the rewards may need to be divided among investors according to their contribution ratio.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract RewardDistribution {
    uint256 public totalContributions;
    mapping(address => uint256) public contributions;
    address[] private investors;

    function contribute() public payable {
        require(msg.value > 0, "Contribution cannot be zero");
        if (contributions[msg.sender] == 0) {
            investors.push(msg.sender);
        }
        contributions[msg.sender] += msg.value;
        totalContributions += msg.value;
    }

    function distributeRewards(uint256 reward) public {
        require(totalContributions > 0, "No contributions to distribute");

        for (uint i = 0; i < investors.length; i++) {
            address investor = investors[i];
            uint256 investorShare = (contributions[investor] * reward) / totalContributions;
            payable(investor).transfer(investorShare);
        }
    }
}

In this example, the distributeRewards function calculates each investor’s share of the reward (investorShare) based on their contributions and the total contributions, using the division operator (/).


Best Practice: Adding Access Control

In real-world smart contracts, the distributeRewards function should not be callable by just anyone. Without restrictions, malicious users could trigger distribution at the wrong time or repeatedly, leading to unexpected or harmful results.

To protect this function, access control is commonly applied. Here are two common approaches:


1. Restricting to Contract Owner (using onlyOwner)

A simple method is to restrict reward distribution to the contract owner. With the help of OpenZeppelin’s Ownable contract:

import "@openzeppelin/contracts/access/Ownable.sol";

contract RewardDistribution is Ownable {
    uint256 public totalContributions;
    mapping(address => uint256) public contributions;
    address[] private investors;

    function contribute() public payable {
        require(msg.value > 0, "Contribution cannot be zero");
        if (contributions[msg.sender] == 0) {
            investors.push(msg.sender);
        }
        contributions[msg.sender] += msg.value;
        totalContributions += msg.value;
    }

    // Only the owner can call this function
    function distributeRewards(uint256 reward) public onlyOwner {
        require(totalContributions > 0, "No contributions to distribute");

        for (uint i = 0; i < investors.length; i++) {
            address investor = investors[i];
            uint256 investorShare = (contributions[investor] * reward) / totalContributions;
            payable(investor).transfer(investorShare);
        }
    }
}

With onlyOwner, only the contract owner (usually the deployer) can trigger distribution.


2. Role-Based Access Control (using AccessControl)

For more complex scenarios (e.g., DAO governance or multiple administrators), role-based access control is preferable.

import "@openzeppelin/contracts/access/AccessControl.sol";

contract RewardDistribution is AccessControl {
    uint256 public totalContributions;
    mapping(address => uint256) public contributions;
    address[] private investors;

    bytes32 public constant DISTRIBUTOR_ROLE = keccak256("DISTRIBUTOR_ROLE");

    constructor() {
        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
        _grantRole(DISTRIBUTOR_ROLE, msg.sender);
    }

    function contribute() public payable {
        require(msg.value > 0, "Contribution cannot be zero");
        if (contributions[msg.sender] == 0) {
            investors.push(msg.sender);
        }
        contributions[msg.sender] += msg.value;
        totalContributions += msg.value;
    }

    // Only accounts with DISTRIBUTOR_ROLE can call this
    function distributeRewards(uint256 reward) public onlyRole(DISTRIBUTOR_ROLE) {
        require(totalContributions > 0, "No contributions to distribute");

        for (uint i = 0; i < investors.length; i++) {
            address investor = investors[i];
            uint256 investorShare = (contributions[investor] * reward) / totalContributions;
            payable(investor).transfer(investorShare);
        }
    }
}

Here, the contract uses AccessControl to assign roles. Only addresses with the DISTRIBUTOR_ROLE are allowed to distribute rewards. This makes the system more flexible and suitable for decentralized governance.


Conclusion

  • Solidity provides a variety of mathematical operators for smart contract development.
  • The division operator (/) is often used in proportional calculations, such as reward distribution.
  • In practice, access control is critical for sensitive functions like distributeRewards.
    • Use onlyOwner for simple contracts.
    • Use AccessControl for role-based, multi-user, or DAO-based scenarios.

By combining Solidity’s operators with proper security practices, developers can write contracts that are both functional and safe.

Subscribe for New Articles!

Leave a Comment

Your email address will not be published. Required fields are marked *