#21DaysSolidityChallenge Day 7: Mastering Time-Locked Wallets — Ether on Lockdown ⏳💰
🚀 Buy me a coffee! ☕ http://buymeacoffee.com/solidity
👋 Welcome to Day 7 of our Solidity Code Challenge series! Today’s challenge takes us into the fascinating realm of time-locked wallets. You’ll create a smart contract that holds Ether securely and allows the owner to withdraw funds only after a specified time period has elapsed. It’s a great opportunity to explore the power of smart contract-based financial instruments!
Oh, this magical link is just sooo tempting! 🪄✨ Click away, my dear friend. 😉
⏳ Time-Locked Wallets: A Quick Overview
Before we dive into today’s challenge, let’s briefly understand what time-locked wallets are and why they’re valuable:
- Time-Locked Wallets: A time-locked wallet is a smart contract that restricts access to its funds until a predefined time or condition is met. They are used for various purposes, such as holding tokens for a certain period, locking savings, or even implementing vesting schedules.
Now, let’s unlock the secrets of time-locked wallets!
Step 1: Setting Up Your Development Environment
Before we get into coding, ensure you have the following tools and accounts ready:
1. Ethereum Wallet: You’ll need an Ethereum wallet like MetaMask to interact with the Ethereum blockchain.
2. Solidity Compiler: Have the Solidity compiler (solc) installed on your computer or use online Solidity development environments like Remix.
3. Test Network: Choose a test network (e.g., Ropsten, Rinkeby, or Kovan) to deploy and test your time-locked wallet contract without using real Ether.
4. Integrated Development Environment (IDE): Consider using an IDE like Visual Studio Code with Solidity extensions for a smoother coding experience.
Step 2: Designing the Time-Locked Wallet Contract
Let’s create a Solidity smart contract for your time-locked wallet. We’ll name it `TimeLockedWallet.sol`. This contract will allow the owner to deposit Ether and specify a withdrawal time. Funds can only be withdrawn after that time.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract TimeLockedWallet {
address public owner;
uint256 public unlockTime;
constructor(address _owner, uint256 _unlockTime) {
owner = _owner;
unlockTime = _unlockTime;
}
receive() external payable {
require(msg.sender == owner, "Only the owner can deposit Ether");
}
function withdraw() external {
require(msg.sender == owner, "Only the owner can withdraw");
require(block.timestamp >= unlockTime, "Funds are locked until the unlock time");
payable(owner).transfer(address(this).balance);
}
}
In this contract:
- We define two state variables: `owner`, which represents the wallet’s owner, and `unlockTime`, which is the timestamp when funds can be withdrawn.
- The constructor initializes these variables when the contract is created.
- We use a `receive` function to allow the owner to deposit Ether into the wallet. Other addresses are rejected.
- The `withdraw` function allows the owner to withdraw funds, but only if they are the sender (owner) and the current time is greater than or equal to the unlock time.
Step 3: Compiling the Time-Locked Wallet Contract
Compile your time-locked wallet contract using the Solidity compiler. Use the following command in your terminal:
solc - bin - abi TimeLockedWallet.sol
This command generates the bytecode (`TimeLockedWallet.bin`) and ABI (`TimeLockedWallet.abi`) required for contract deployment.
Step 4: Deploying the Time-Locked Wallet Contract
Deploy your time-locked wallet contract to a test network. Follow these steps:
1. Open your Ethereum wallet (e.g., MetaMask) and switch to the Ropsten network.
2. Acquire some test Ether for Ropsten from a faucet. You can find a faucet by searching “Ropsten faucet” online.
3. Deploy your contract using a tool like Remix or Truffle, or manually through a script.
- Go to Remix (https://remix.ethereum.org/).
- Click the “Solidity” tab.
- Create a new file, paste your time-locked wallet contract code, and compile it.
- Switch to the “Deploy & Run Transactions” tab.
- Ensure your environment is set to “Injected Web3” (if you’re using MetaMask).
- Click the “Deploy” button.
4. Confirm the deployment in your wallet, and your contract will be deployed to the Ropsten network.
Step 5: Testing the Time-Locked Wallet
Now that your time-locked wallet contract is deployed, let’s test its functionality:
1. In Remix, locate your deployed `TimeLockedWallet` contract.
2. You will see the contract functions, including `withdraw`.
3. To simulate time-locking, set a reasonably long unlock time, for example, 24 hours in the future. You can calculate the UNIX timestamp for this time.
4. Use the `receive` function to deposit some test Ether into the wallet.
5. Attempt to withdraw Ether before the unlock time. You should receive an error message.
6. Wait until the unlock time has passed.
7. Try to withdraw Ether again. This time, the transaction should succeed, and the Ether will be transferred to the owner’s address.
Step 6: Writing Tests for the Time-Locked Wallet
To ensure that the withdrawal restriction is enforced correctly, let’s write tests for the time-locked wallet contract. You can use a testing framework like Truffle or Hardhat. Here’s a simplified example using Truffle:
// In a Truffle test file, e.g., TimeLockedWallet.test.js
const TimeLockedWallet = artifacts.require("TimeLockedWallet");
contract("TimeLockedWallet", (accounts) => {
it("should lock funds until the unlock time", async () => {
const owner = accounts[0];
const unlockTime = Math.floor(Date.now() / 1000) + 3600; // 1 hour in the future
const wallet = await TimeLockedWallet.new(owner, unlockTime);
const initialBalance = web3.utils.toBN(await web3.eth.getBalance(wallet.address));
// Attempt to withdraw before unlock time
try {
await wallet.withdraw({ from: owner });
} catch (error) {
assert.include(error.message, "revert", "Withdrawal should revert");
}
// Wait until after the unlock time
await new Promise(resolve => setTimeout(resolve, 3600 * 1000)); // Wait for 1 hour
// Withdraw after unlock time
const tx = await wallet.withdraw({ from: owner });
const gasUsed = web3.utils.toBN(await web3.eth.getTransactionReceipt(tx.tx).then(receipt => receipt.gasUsed));
const finalBalance = web3.utils.toBN(await web3.eth.getBalance(wallet.address));
assert.equal(finalBalance.toString(), "0", "Wallet should be empty after withdrawal");
assert.isAbove(initialBalance.sub(gasUsed).toNumber(), 0, "Owner should receive funds after withdrawal");
});
});
This Truffle test checks that the funds are locked until the unlock time and can be withdrawn afterward. Make sure to adjust the unlock
time and timing logic according to your needs.
Step 7: Running the Tests
Execute the tests using the Truffle framework. In your project directory, run:
truffle test
This command will run your tests and verify that the time-locked wallet enforces the withdrawal restriction correctly.
Conclusion 🌟
In this Day 7 challenge, you’ve mastered the art of creating time-locked wallets. These smart contracts provide a secure way to lock and release funds based on a predefined schedule, offering various use cases in the world of decentralized finance.
As you progress through the Solidity Code Challenge series, you’ll continue to explore advanced topics and build increasingly sophisticated smart contracts. Keep up the fantastic work, and keep unlocking the potential of blockchain technology!
🚀 Happy coding and time-locking! 🚀