0

How can I set a timer that will automatically execute a function after an arbitrary amount of time while also allowing the contract to concurrently perform other actions?

I have a smart contract that runs elections. It is supposed to be able to end the election by setting election.active = false after an arbitrary amount of time. The problem is, if I set that timer, then the code won't be able to execute anything until that timer runs out:

function startElection(uint guildID, uint initiator, uint role, uint duration) public {
        // this starts the election where users can join to campaign

        uint electionID = uint(keccak256(abi.encodePacked(initiator, role, duration, block.timestamp)));

        uint startTime = block.timestamp;
        uint endTime = startTime + duration;

        elections[electionID] = Election(true, guildID, initiator, role, duration, endTime);

        emit ElectionInitiated(electionID, guildID, initiator, role, duration, endTime);

        watch(electionID); // by executing this function, the contract will get stuck here until the timer runs out
    }

  function watch(uint electionID) internal view {
        Election memory election = elections[electionID];


        while (block.timestamp < election.endTime) {}
        // now end the election here
        election.active = false; // ends the election

        
        emit ElectionEnded(electionID); // emits an event telling me the election has ended
    }

1 Answer 1

0

You can utilize Chainlink Automation to automate your particular function calls.

There are currently three types of triggers that the Chainlink Automation Network supports including:

As per your described use-case, the Custom logic trigger can be used to execute the functionality of the watch() function of your smart contract, by replacing it with the checkUpkeep() and performUpkeep() functions of the inherited AutomationCompatibleInterface.

Here's the sample snippet for the same:

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

import {AutomationCompatibleInterface} from "@chainlink/contracts/src/v0.8/automation/AutomationCompatible.sol";

contract AutomatedElection is AutomationCompatibleInterface {
    struct Election {
        bool active;
        uint256 guildID;
        uint256 initiator;
        uint256 role;
        uint256 duration;
        uint256 endTime;
    }

    event ElectionInitiated(
        uint256 electionID, uint256 guildID, uint256 initiator, uint256 role, uint256 duration, uint256 endTime
    );
    event ElectionEnded(uint256 electionID);

    mapping(uint256 electionID => Election) public elections;

    uint256[] public electionIDs;

    function startElection(uint256 guildID, uint256 initiator, uint256 role, uint256 duration) public {
        // this starts the election where users can join to campaign

        uint256 electionID = uint256(keccak256(abi.encodePacked(initiator, role, duration, block.timestamp)));

        uint256 startTime = block.timestamp;
        uint256 endTime = startTime + duration;

        electionIDs.push(electionID);

        elections[electionID] = Election(true, guildID, initiator, role, duration, endTime);

        emit ElectionInitiated(electionID, guildID, initiator, role, duration, endTime);
    }

    function checkUpkeep(bytes memory /* checkdata */ )
        public
        view
        override
        returns (bool upkeepNeeded, bytes memory performData)
    {
        for (uint256 i = 0; i < electionIDs.length; i++) {
            Election memory election = elections[electionIDs[i]];
            if (block.timestamp > election.endTime && election.active) {
                upkeepNeeded = true;
                performData = abi.encode(electionIDs[i]);
                return (upkeepNeeded, performData);
            }
        }
    }

    function performUpkeep(bytes calldata /* performData */ ) external override {
        (bool upkeepNeeded, bytes memory performData) = checkUpkeep("");
        if (upkeepNeeded) {
            uint256 electionID = abi.decode(performData, (uint256));
            elections[electionID].active = false; // ends the election
            emit ElectionEnded(electionID); // emits an event telling me the election has ended
        }
    }
}
2
  • will this solution work on local testnets? Commented Aug 6 at 14:52
  • It works on testnets like Eth-Sepolia, Avalanche Fuji, Polygon Amoy, etc. But to run locally on Anvil or Hardhat local networks, you need to manually write tests for calling the performUpkeep function, as there is no Chainlink Automation node serving locally. Commented Aug 6 at 15:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.