-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
The Recurrent Script runs a command every so often. It uses StateManager to track the last time it executed, and it allows the script to be executed after some interval post-that. To help the user, we add a `notBefore` and `notAfter` set of functions that can be used to shape the overall time curve, e.g. to expire a transaction after some time. (Though this could be accomplished with expiration on the QuarkOperation itself, it seems like this is fairly important here).
- Loading branch information
Showing
1 changed file
with
60 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
// SPDX-License-Identifier: BSD-3-Clause | ||
pragma solidity 0.8.23; | ||
|
||
import {QuarkScript} from "quark-core/src/QuarkScript.sol"; | ||
|
||
/** | ||
* @title Recurrent Core Script | ||
* @notice Core transaction script that can be used to call a function every so often | ||
* @author Compound Labs, Inc. | ||
*/ | ||
contract Recurrent is QuarkScript { | ||
error InvalidCallContext(); | ||
error TooFrequent(uint256 lastExecution); | ||
error Premature(); | ||
error Expired(); | ||
|
||
/// @notice This contract's address | ||
address internal immutable scriptAddress; | ||
|
||
/** | ||
* @notice Constructor | ||
*/ | ||
constructor() { | ||
scriptAddress = address(this); | ||
} | ||
|
||
/** | ||
* @notice Execute a single call | ||
* @dev Note: Does not use a reentrancy guard, so make sure to only call into trusted contracts | ||
* @param callContract Contract to call | ||
* @param callData Encoded calldata for call | ||
* @param interval Interval for the call in seconds (i.e. every X seconds) | ||
* @param notBefore Do not run this script before this time. | ||
* @param notAfter Do not run this script after this time. | ||
* @return Return data from call | ||
*/ | ||
function run(address callContract, bytes calldata callData, uint256 interval, uint256 notBefore, uint256 notAfter) external returns (bytes memory) { | ||
if (address(this) == scriptAddress) { | ||
revert InvalidCallContext(); | ||
} | ||
|
||
// Note: this starts out as zero, as in | ||
uint256 lastExecution = readU256("lastExecution"); | ||
if (block.timestamp < lastExecution + interval) revert TooFrequent(lastExecution); | ||
if (block.timestamp < notBefore) revert Premature(); | ||
if (block.timestamp > notAfter) revert Expired(); | ||
writeU256("lastExecution", block.timestamp); | ||
|
||
(bool success, bytes memory returnData) = callContract.delegatecall(callData); | ||
if (!success) { | ||
assembly { | ||
revert(add(returnData, 32), mload(returnData)) | ||
} | ||
} | ||
|
||
allowReplay(); | ||
|
||
return returnData; | ||
} | ||
} |