Created
August 3, 2022 22:45
-
-
Save ArpitxGit/16893b2ae82953bbfe7e9b1d3f6745ec to your computer and use it in GitHub Desktop.
Delaying Transaction with Time Lock
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
//SPDX-License-Identifier: MIT | |
pragma solidity ^0.8.13; | |
/* | |
Contract to Delay Transaction | |
"for security puposes or taking fail safe measuers | |
as users trust contract more than anything." | |
1. You are going to broadcast a transaction | |
that you are going to execute, | |
with the help of function queue(). | |
2. Once a transaction is queued, | |
you have to wait a certain amount of time, | |
depends on you, as you will be difining. | |
3. Once the defined time has passed, | |
the transaction will be executed, | |
with the help of function execute(). | |
*/ | |
contract TimeLock { | |
error NotOwnerError(); | |
error AlreadyQueuedError(bytes32 txId); | |
error TimestampNotInRangeError(uint blockTimestamp, uint timestamp); | |
error NotQueuedError(bytes32 txId); | |
error TimestampNotPassedError(uint blockTimestamp, uint timestamp); | |
error TimestampExpiredError(uint blockTimestamp, uint expiresAt); | |
error TxFailedError(); | |
event Queue( | |
bytes32 indexed txId, | |
address indexed target, | |
uint value, | |
string func, | |
bytes data, | |
uint timestamp | |
); | |
event Execute( | |
bytes32 indexed txId, | |
address indexed target, | |
uint value, | |
string func, | |
bytes data, | |
uint timestamp | |
); | |
event Cancel(bytes32 indexed txId); | |
uint public constant MIN_DELAY = 10;//10seconds | |
uint public constant MAX_DELAY = 1000;//1000seconds | |
uint public constant GRACE_PERIOD = 1000;//1000seconds | |
//so we have now 1000 seconds before the tx is expired | |
address public owner; | |
mapping(bytes32 => bool) public queued; | |
constructor() { | |
owner = msg.sender; | |
} | |
modifier onlyOwner() { | |
if(msg.sender != owner){ | |
revert NotOwnerError(); | |
} | |
_; | |
} | |
//defining a fallback receive function | |
receive() external payable {} | |
//function to compute tx id | |
function getTxId( | |
address _target, | |
uint _value, | |
string calldata _func, | |
bytes calldata _data, | |
uint _timestamp | |
) public pure returns (bytes32 txId) { | |
return keccak256(abi.encode(_target, _value, _func, _data, _timestamp)); | |
} | |
/** | |
* @param _target Address of contract or account to call | |
* @param _value Amount of ETH to send | |
* @param _func Function signature, for example "foo(address,uint256)" | |
* @param _data ABI encoded data send. | |
* @param _timestamp Timestamp after which the transaction can be executed. | |
*/ | |
//function for queuing the trasaction | |
function queue( | |
address _target, | |
uint _value, | |
string calldata _func, | |
bytes calldata _data, | |
uint _timestamp | |
) external onlyOwner returns (bytes32 txId) { | |
//create tx id | |
//after creating tx id, we will check that this tx id has not been queued yet | |
//so creating a mapping on top of tx id with boolean | |
//if tx id is queued then boolean will be true | |
txId = getTxId(_target, _value, _func, _data, _timestamp); | |
//checking tx id unique | |
if(queued[txId]) { | |
revert AlreadyQueuedError(txId); | |
} | |
//check timestamp | |
// ---|----------|------here-----|------- | |
// block block+min block+max | |
//we have to make sure that our timestamp fits between block+min and block+max | |
if(_timestamp < block.timestamp + MIN_DELAY || _timestamp > block.timestamp + MAX_DELAY){ | |
revert TimestampNotInRangeError(block.timestamp, _timestamp); | |
} | |
//queue tx | |
queued[txId] = true; | |
//emit the event | |
emit Queue(txId, _target, _value, _func, _data, _timestamp); | |
} | |
//for executing the transaction only by the owner | |
function execute( | |
address _target, | |
uint _value, | |
string calldata _func, | |
bytes calldata _data, | |
uint _timestamp | |
) external payable onlyOwner returns (bytes memory) { | |
bytes32 txId = getTxId(_target, _value, _func, _data, _timestamp); | |
//chech if tx is queued | |
if(!queued[txId]) { | |
revert NotQueuedError(txId); | |
} | |
//check correct time i.e block.timestamp > _timestamp | |
if(block.timestamp < _timestamp) { | |
revert TimestampNotPassedError(block.timestamp, _timestamp); | |
} | |
//what if transaction got expired | |
//so introducing grace period | |
//----|-----valid tx----|------in this period tx is expired | |
//timestamp timestamp + grace period | |
if(block.timestamp > _timestamp + GRACE_PERIOD){ | |
revert TimestampExpiredError(block.timestamp, _timestamp + GRACE_PERIOD); | |
} | |
//delete tx from queue | |
queued[txId] = false; | |
//data to be passed to execute tx, if function is empty then passing data | |
//if not then abi encode the function and appending the data | |
//taking the first 4 bytes | |
bytes memory data; | |
if(bytes(_func).length > 0) { | |
data = abi.encodePacked(bytes4(keccak256(bytes(_func))), _data); | |
} else { | |
data = _data; | |
} | |
//execute the tx | |
(bool ok, bytes memory res) = _target.call{value: _value}(data); | |
if (!ok) { | |
revert TxFailedError(); | |
} | |
emit Execute(txId, _target, _value, _func, _data, _timestamp); | |
return res; | |
} | |
//function to cancel a tx | |
function cancel(bytes32 _txId) external onlyOwner { | |
if(!queued[_txId]) { | |
revert NotQueuedError(_txId); | |
} | |
queued[_txId] = false; | |
emit Cancel(_txId); | |
} | |
} | |
contract TestTimeLock { | |
address public timeLock; | |
constructor(address _timeLock) { | |
_timeLock = timeLock; | |
} | |
function test() external { | |
//only be called by the TimeLock contract | |
require(msg.sender == timeLock); | |
//also can use to | |
//upgrade the contract | |
//funds transafer | |
//switch price oracle | |
} | |
//helper function | |
function getTimestamp() external view returns (uint) { | |
return block.timestamp + 100; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment