這篇文章在時間上介紹智能合約中是什麼,講解如何開發請求。並且你可以按一個智能合約來開發,這個合約可能會導致ERC-20 通證的時間排序。
本教程使用到:
教程的代碼可以在這個 GitHub 回購 中找。
什麼是智能合約的時間鎖
複本,代碼鎖是使用鎖鏈的時間函數就可以鎖定在智能裡面的。 “如果”:
if (block.timestamp < _timelockTime) { revert ErrorNotReady(block.timestamp, _timelockTime); }
時間鎖的應用場景
智能合約中有很多潛在的場景,它們通常會在公開發行中使用通證功能,用於實現通證銷售一些。時間鎖也可以被用於按照使用時間的收益投資的投資用途使用,即用戶只有在以後才可以使用資金。
可能的情況就是通過契約去實現遺囑。使用Chainlink Keepers,你可以單獨檢查這個遺囑主人是否已經死亡,有可能證明被遺囑遺囑的智能發布契約可以促成。
以上只是很少的一些應用案例,智能合約鎖有很多去場景使用。在這個案例中,我們會聚焦在一個 ERC-20 合同,用時間鎖實現一個結果來製造它。
怎樣創建一個智能時間鎖
在這個教程中,我們會使用Foundry 來開發和測試Solidity 合約。關於Foundry 這個框架,你可以是它的 GitHub 中查找更多信息。
初始化項目
你可以使用`forge init`初始化問題初始化項目。項目完成後,`forge test`命令會進行一次檢查確保項目初始化的過程沒有。
❯ forge init timelocked-contract Initializing /Users/rg/Development/timelocked-contract... Installing ds-test in "/Users/rg/Development/timelocked-contract/lib/ds-test", (url: https://github.com/dapphub/ds-test, tag: None) Installed ds-test Initialized forge project. ❯ cd timelocked-contract ❯ forge test [⠒] Compiling... [⠰] Compiling 3 files with 0.8.10 [⠔] Solc finished in 143.06ms Compiler run successful Running 1 test for src/test/Contract.t.sol:ContractTest [PASS] testExample() (gas: 190) Test result: ok. 1 passed; 0 failed; finished in 469.71µs
創建測試
你需要創建一些測試來確保智能合約可以實現時間鎖定的所有要求。需要測試的主要功能如下:
- 讓通證的操作要素參與
- 時間到來就進行鑄造
- 取消早在中的操作
除了這些功能之外,您還需要保證智能合約沒有重複入列或入列之前生成這些錯誤操作。
當項目被初始化這些之後,你需要去運行這些測試,因為你需要這些來保證你的項目的實際執行和預測的測試,不需要你用例。測試在`src/test/Contract.t.sol`中。在Foundry 中,使用會測試的名字表示這些應該是成功還是失敗了。 `testThisShouldWork` 表示應該通過,而`testFailShouldNotWork` 表示只有來測試恢復這個時候才會被測試通過。
一些其他的使用情況。時間鎖會基於一個時段,這個節目會使用`_toress`、`_地址`和`_參數的哈希值`,而`keccak256`會計算它們的時間`的哈希值。
// Create hash of transaction data for use in the queue function generateTxnHash( address _to, uint256 _amount, uint256 _timestamp ) public pure returns (bytes32) { return keccak256(abi.encode(_to, _amount, _timestamp)); }
另外,你需要自己設置測試環境中的時間來模擬有多少時間過去。這點可以通過Foundry 的`CheatCode` 實現。
interface CheatCodes { function warp(uint256) external; }
wrap 設置當前的這個函數可以讓你使用這個函數。可以讓這個函數接受一個參數來生成新的時間指示。我們需要它給來“增加時間”,模擬時間的流。可以讓我們在測試中按照預期時間提供鎖定功能所需要的變量。
把`src/test/Contract.t.sol`中的內容替換為下面的代碼:
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.10; import "ds-test/test.sol"; import "../Contract.sol"; interface CheatCodes { function warp(uint256) external; } contract ContractTest is DSTest { // HEVM_ADDRESS is the pre-defined contract that contains the cheatcodes CheatCodes constant cheats = CheatCodes(HEVM_ADDRESS); Contract public c; address toAddr = 0x1234567890123456789012345678901234567890; function setUp() public { c = new Contract(); c.queueMint( toAddr, 100, block.timestamp + 600 ); } // Ensure you can't double queue function testFailDoubleQueue() public { c.queueMint( toAddr, 100, block.timestamp + 600 ); } // Ensure you can't queue in the past function testFailPastQueue() public { c.queueMint( toAddr, 100, block.timestamp - 600 ); } // Minting should work after the time has passed function testMintAfterTen() public { uint256 targetTime = block.timestamp + 600; cheats.warp(targetTime); c.executeMint( toAddr, 100, targetTime ); } // Minting should fail if you mint too soon function testFailMintNow() public { c.executeMint( toAddr, 100, block.timestamp + 600 ); } // Minting should fail if you didn't queue function testFailMintNonQueued() public { c.executeMint( toAddr, 999, block.timestamp + 600 ); } // Minting should fail if try to mint twice function testFailDoubleMint() public { uint256 targetTime = block.timestamp + 600; cheats.warp(block.timestamp + 600); c.executeMint( toAddr, 100, targetTime ); c.executeMint( toAddr, 100, block.timestamp + 600 ); } // Minting should fail if you try to mint too late function testFailLateMint() public { uint256 targetTime = block.timestamp + 600; cheats.warp(block.timestamp + 600 + 1801); emit log_uint(block.timestamp); c.executeMint( toAddr, 100, targetTime ); } // you should be able to cancel a mint function testCancelMint() public { bytes32 txnHash = c.generateTxnHash( toAddr, 100, block.timestamp + 600 ); c.cancelMint(txnHash); } // you should be able to cancel a mint once but not twice function testFailCancelMint() public { bytes32 txnHash = c.generateTxnHash( toAddr, 999, block.timestamp + 600 ); c.cancelMint(txnHash); c.cancelMint(txnHash); } // you shouldn't be able to cancel a mint that doesn't exist function testFailCancelMintNonQueued() public { bytes32 txnHash = c.generateTxnHash( toAddr, 999, block.timestamp + 600 ); c.cancelMint(txnHash); } }
開發合同
你現在應該可以執行命令偽造測試,然後看到許多錯誤,就讓我們使這些測試可以被通過。我們現在從一個最基礎的ERC-20 合約開始,所有的代碼都存儲在`src/Contract.sol `中。
// SPDX-License-Identifier: MIT pragma solidity ^0.8.10; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; contract Contract is ERC20, Ownable { constructor() ERC20("TimeLock Token", "TLT") {} }
這裡在使用OpenZeppelin 合約之前,你需要先安裝它,並且將Foundry 指向它們。
運行以下命令,這些安裝合約:
❯ forge install openzeppelin/openzeppelin-contracts
創建`remappings.txt` 來映射這些導入
@openzeppelin/=lib/openzeppelin-contracts/ ds-test/=lib/ds-test/src/
重新映射文件讓你像在Hardhat 中使用類似OpenZeppelin 契約的外部文件,或者將這個文件安裝到OpenZeppelin 類似的外部文件庫中,因為這個文件可以重新映射到我他們所在的文件夾。通過`forge install openzeppelin/openzeppelin-contracts` `OpenZeppelin合同,他們會被用來創建ERC-721 合同。
如果一切順利的話,你可以運行`forge build`來編譯合約。
完成操作以後,窗口就可以讓你將一個創建操作加入執行,然後在這個操作的時候,這個窗口就可以編寫這個操作。
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.10; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; contract Contract is ERC20, Ownable { // Error Messages for the contract error ErrorAlreadyQueued(bytes32 txnHash); error ErrorNotQueued(bytes32 txnHash); error ErrorTimeNotInRange(uint256 blockTimestmap, uint256 timestamp); error ErrorNotReady(uint256 blockTimestmap, uint256 timestamp); error ErrorTimeExpired(uint256 blockTimestamp, uint256 expiresAt); // Queue Minting Event event QueueMint( bytes32 indexed txnHash, address indexed to, uint256 amount, uint256 timestamp ); // Mint Event event ExecuteMint( bytes32 indexed txnHash, address indexed to, uint256 amount, uint256 timestamp ); // Cancel Mint Event event CancelMint(bytes32 indexed txnHash); // Constants for minting window uint256 public constant MIN_DELAY = 60; // 1 minute uint256 public constant MAX_DELAY = 3600; // 1 hour uint256 public constant GRACE_PERIOD = 1800; // 30 minutes // Minting Queue mapping(bytes32 => bool) public mintQueue; constructor() ERC20("TimeLock Token", "TLT") {} // Create hash of transaction data for use in the queue function generateTxnHash( address _to, uint256 _amount, uint256 _timestamp ) public pure returns (bytes32) { return keccak256(abi.encode(_to, _amount, _timestamp)); } // Queue a mint for a given address amount, and timestamp function queueMint( address _to, uint256 _amount, uint256 _timestamp ) public onlyOwner { // Generate the transaction hash bytes32 txnHash = generateTxnHash(_to, _amount, _timestamp); // Check if the transaction is already in the queue if (mintQueue[txnHash]) { revert ErrorAlreadyQueued(txnHash); } // Check if the time is in the range if ( _timestamp < block.timestamp + MIN_DELAY || _timestamp > block.timestamp + MAX_DELAY ) { revert ErrorTimeNotInRange(_timestamp, block.timestamp); } // Queue the transaction mintQueue[txnHash] = true; // Emit the QueueMint event emit QueueMint(txnHash, _to, _amount, _timestamp); } // Execute a mint for a given address, amount, and timestamp function executeMint( address _to, uint256 _amount, uint256 _timestamp ) external onlyOwner { // Generate the transaction hash bytes32 txnHash = generateTxnHash(_to, _amount, _timestamp); // Check if the transaction is in the queue if (!mintQueue[txnHash]) { revert ErrorNotQueued(txnHash); } // Check if the time has passed if (block.timestamp < _timestamp) { revert ErrorNotReady(block.timestamp, _timestamp); } // Check if the window has expired if (block.timestamp > _timestamp + GRACE_PERIOD) { revert ErrorTimeExpired(block.timestamp, _timestamp); } // Remove the transaction from the queue mintQueue[txnHash] = false; // Execute the mint mint(_to, _amount); // Emit the ExecuteMint event emit ExecuteMint(txnHash, _to, _amount, _timestamp); } // Cancel a mint for a given transaction hash function cancelMint(bytes32 _txnHash) external onlyOwner { // Check if the transaction is in the queue if (!mintQueue[_txnHash]) { revert ErrorNotQueued(_txnHash); } // Remove the transaction from the queue mintQueue[_txnHash] = false; // Emit the CancelMint event emit CancelMint(_txnHash); } // Mint tokens to a given address function mint(address to, uint256 amount) internal { _mint(to, amount); } }
可以做什麼直播
智能合約的時間鎖非常有用,它們可以讓合約內的交易安全和透明。但是時間無法觸發非常的話,所以你需要在某個時間自動執行這個函數。 ,就需要自動化你的合同。
鏈環守護者 可以讓你自動化智能合約中的函數。通過使用Chainlink Keepers,你可以在某些提前定義好的時間節點,讓你智能合約中的函數執行。想要了解更多關於Chainlink Keepers 的信息,請查看 守護者文檔。
您可以關注鏈家禽機資料和私信加入者社區,有大量關於智能合約開發的學習以及關於區塊鏈的話題!
帖子 怎樣開發智能合約中的時間鎖 首先出現在 Chainlink 博客.