-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Recurrent Script #174
base: main
Are you sure you want to change the base?
Add Recurrent Script #174
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Kind of interesting thing is that you can have multiple replayable txns using the same nonce with different calldata because Quark allows you to do that now. |
||
if (address(this) == scriptAddress) { | ||
revert InvalidCallContext(); | ||
} | ||
|
||
// Note: this starts out as zero, as in | ||
uint256 lastExecution = readU256("lastExecution"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: Minor optimization can be to use a 1 char key instead. You can store it as a constant to keep the same readability. |
||
if (block.timestamp < lastExecution + interval) revert TooFrequent(lastExecution); | ||
Comment on lines
+43
to
+44
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the value is zero, don't we need to set it first? As i understand, this will always revert the first time unless There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @banky I think you have that logic inversed. It shouldn't revert if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ohh yeah that's right haha. i was reading it as if it was |
||
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; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could consider adding a
cancel()
function that just cancels the nonce. See theRecurringPurchase
example.