前言本文主要利用哈希时间锁合约(HashTimeLockedContract,HTLC)是一种去中心化的合约机制,通过结合时间锁和哈希锁,实现了条件支付的功能,包含了开发、测试、部署全部流程;区块链上的时间锁定义:一种去中心化的合约机制,通过时间锁和哈希锁的结合从而实现。工作流程创
本文主要利用哈希时间锁合约(Hash Time Locked Contract,HTLC)是一种去中心化的合约机制,通过结合时间锁和哈希锁,实现了条件支付的功能,包含了开发、测试、部署全部流程;
定义:一种去中心化的合约机制,通过时间锁和哈希锁的结合从而实现。<br>
场景:主要用在DeFi
和DAO
中,可以提高项目的安全性;
功能:智能合约的某些功能锁定一段时间,大大减少项目方rug pull
和黑客攻击的机会,增加去中心化应用的安全性;
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
import "hardhat/console.sol";
contract Timelock{
// 事件
// 交易取消事件
event CancelTransaction(bytes32 indexed txHash, address indexed target, uint value, string signature, bytes data, uint executeTime);
// 交易执行事件
event ExecuteTransaction(bytes32 indexed txHash, address indexed target, uint value, string signature, bytes data, uint executeTime);
// 交易创建并进入队列 事件
event QueueTransaction(bytes32 indexed txHash, address indexed target, uint value, string signature, bytes data, uint executeTime);
// 修改管理员地址的事件
event NewAdmin(address indexed newAdmin);
// 状态变量
address public admin; // 管理员地址
uint public constant GRACE_PERIOD = 7 days; // 交易有效期,过期的交易作废
uint public delay; // 交易锁定时间 (秒)
mapping (bytes32 => bool) public queuedTransactions; // txHash到bool,记录所有在时间锁队列中的交易
// onlyOwner modifier
modifier onlyOwner() {
require(msg.sender == admin, "Timelock: Caller not admin");
_;
}
// onlyTimelock modifier
modifier onlyTimelock() {
require(msg.sender == address(this), "Timelock: Caller not Timelock");
_;
}
/**
* @dev 构造函数,初始化交易锁定时间 (秒)和管理员地址
*/
constructor(uint delay_) {
delay = delay_;
admin = msg.sender;
}
/**
* @dev 改变管理员地址,调用者必须是Timelock合约。
*/
function changeAdmin(address newAdmin) public onlyTimelock {
admin = newAdmin;
emit NewAdmin(newAdmin);
}
/**
* @dev 创建交易并添加到时间锁队列中。
* @param target: 目标合约地址
* @param value: 发送eth数额
* @param signature: 要调用的函数签名(function signature)
* @param data: call data,里面是一些参数
* @param executeTime: 交易执行的区块链时间戳
*
* 要求:executeTime 大于 当前区块链时间戳+delay
*/
function queueTransaction(address target, uint256 value, string memory signature, bytes memory data, uint256 executeTime) public onlyOwner returns (bytes32) {
// 检查:交易执行时间满足锁定时间
require(executeTime >= getBlockTimestamp() + delay, "Timelock::queueTransaction: Estimated execution block must satisfy delay.");
// 计算交易的唯一识别符:一堆东西的hash
bytes32 txHash = getTxHash(target, value, signature, data, executeTime);
// 将交易添加到队列
queuedTransactions[txHash] = true;
emit QueueTransaction(txHash, target, value, signature, data, executeTime);
return txHash;
}
/**
* @dev 取消特定交易。
*
* 要求:交易在时间锁队列中
*/
function cancelTransaction(address target, uint256 value, string memory signature, bytes memory data, uint256 executeTime) public onlyOwner{
// 计算交易的唯一识别符:一堆东西的hash
bytes32 txHash = getTxHash(target, value, signature, data, executeTime);
// 检查:交易在时间锁队列中
require(queuedTransactions[txHash], "Timelock::cancelTransaction: Transaction hasn't been queued.");
// 将交易移出队列
queuedTransactions[txHash] = false;
emit CancelTransaction(txHash, target, value, signature, data, executeTime);
}
/**
* @dev 执行特定交易。
*
* 要求:
* 1. 交易在时间锁队列中
* 2. 达到交易的执行时间
* 3. 交易没过期
*/
function executeTransaction(address target, uint256 value, string memory signature, bytes memory data, uint256 executeTime) public payable onlyOwner returns (bytes memory) {
bytes32 txHash = getTxHash(target, value, signature, data, executeTime);
// 检查:交易是否在时间锁队列中
require(queuedTransactions[txHash], "Timelock::executeTransaction: Transaction hasn't been queued.");
// 检查:达到交易的执行时间
require(getBlockTimestamp() >= executeTime, "Timelock::executeTransaction: Transaction hasn't surpassed time lock.");
// 检查:交易没过期
require(getBlockTimestamp() <= executeTime + GRACE_PERIOD, "Timelock::executeTransaction: Transaction is stale.");
// 将交易移出队列
queuedTransactions[txHash] = false;
// 获取call data
bytes memory callData;
if (bytes(signature).length == 0) {
callData = data;
} else {
// 这里如果采用encodeWithSignature的编码方式来实现调用管理员的函数,请将参数data的类型改为address。不然会导致管理员的值变为类似"0x0000000000000000000000000000000000000020"的值。其中的0x20是代表字节数组长度的意思.
callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data);
}
// 利用call执行交易
(bool success, bytes memory returnData) = target.call{value: value}(callData);
require(success, "Timelock::executeTransaction: Transaction execution reverted.");
emit ExecuteTransaction(txHash, target, value, signature, data, executeTime);
return returnData;
}
/**
* @dev 获取当前区块链时间戳
*/
function getBlockTimestamp() public view returns (uint) {
return block.timestamp;
}
/**
* @dev 将一堆东西拼成交易的标识符
*/
function getTxHash(
address target,
uint value,
string memory signature,
bytes memory data,
uint executeTime
) public pure returns (bytes32) {
return keccak256(abi.encode(target, value, signature, data, executeTime));
}
}
# 编译指令
# npx hardhat compile
说明:1.获取当前区块时间来模拟延迟执行,2.通过ethers.utils.defaultAbiCoder.encode(["address"], [newAdmin])方法获取hash或者使用在线工具生成abiHash
const {ethers,getNamedAccounts,deployments} = require("hardhat");
const { assert,expect } = require("chai");
describe("Timelock",function(){
let Timelock;//合约地址
let firstAccount//第一个账户
let secondAccount//第二个账户
let addr1;
let addr2;
beforeEach(async function(){
await deployments.fixture(["Timelock"]);
[addr1,addr2]=await ethers.getSigners();
firstAccount=(await getNamedAccounts()).firstAccount;
secondAccount=(await getNamedAccounts()).secondAccount;
const TimelockDeployment = await deployments.get("Timelock");
Timelock = await ethers.getContractAt("Timelock",TimelockDeployment.address);//已经部署的合约交互
});
describe("时间锁",function(){
it("时间锁测试",async function(){
// 获取当前区块时间
const currentTime = await ethers.provider.getBlock("latest").then(block => block.timestamp);
// 预定执行时间:当前时间 + 100s
const executeTime = currentTime + 100;
console.log("执行时间",executeTime)
//时间未到执行报错
// console.log('时间未到报错',await Timelock.changeAdmin())
console.log("admin的地址",await Timelock.admin());
//放入时间对列里
let TimelockAddress=Timelock.address;//合约地址
let value=0;//代币数量
const newAdmin = "0x70997970C51812dc3A010C7d01b50e0d17dc79C8";
//生成hash值 方法1
let data=ethers.utils.defaultAbiCoder.encode(["address"], [newAdmin]);//生成hash
console.log(data)
//生成hash值 方法2 借助在线工具生成
let data1="0xec86135600000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c8";//可以通过在线工具生成,newAdmin
let changeAdmin="changeAdmin(address)";//方法
//添加到时间队列里 参数说明:合约地址,代币数量,方法,hash,执行时间
await Timelock.queueTransaction(TimelockAddress,value,changeAdmin,data,executeTime)
console.log("添加到时间队列里")
//模拟100s后修改管理员
await ethers.provider.send("evm_increaseTime", [100]);
await ethers.provider.send("evm_mine", []);
//执行交易参数说明:合约地址,代币数量,方法,hash,执行时间
await Timelock.executeTransaction(TimelockAddress,value,changeAdmin,data,executeTime)
console.log("修改后admin的地址",await Timelock.admin());
})
})
})
# 测试指令
# npx hardhat test ./test/xxx.js
module.exports = async function ({getNamedAccounts,deployments}) {
const firstAccount= (await getNamedAccounts()).firstAccount;
const secondAccount= (await getNamedAccounts()).secondAccount;
const {deploy,log}=deployments;
const Timelock=await deploy("Timelock",{
from:firstAccount,
args: [100],//参数 锁定时间
log: true,
})
console.log('TokenLocker合约地址',Timelock.address)
}
module.exports.tags = ["all", "Timelock"];
# 部署指令
# npx hardhat deploy
以上就是时间锁合约从开发、测试、部署全过程以及对时间锁概念以及使用场景等相关介绍;
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!