diff --git a/contracts/misc/TimeLock.sol b/contracts/misc/TimeLock.sol new file mode 100644 index 00000000..133164b2 --- /dev/null +++ b/contracts/misc/TimeLock.sol @@ -0,0 +1,26 @@ +pragma solidity ^0.5.17; + + +contract TimeLock { + + address public owner; + uint256 public releaseTime; + + constructor(address _owner, uint256 _releaseTime) public { + owner = _owner; + releaseTime = _releaseTime; + } + + function () external payable { + } + + function withdraw() external { + require(msg.sender == owner, "only owner can withdraw"); + // solhint-disable-next-line not-rely-on-time + require(releaseTime < now, "cannot withdraw before releaseTime"); + // solhint-disable-next-line avoid-call-value + (bool success, ) = owner.call.value(address(this).balance)(""); + require(success, "sendEther failed."); + } + +} diff --git a/contracts/test/Wallet.sol b/contracts/test/Wallet.sol index 4d509f82..31d94a36 100644 --- a/contracts/test/Wallet.sol +++ b/contracts/test/Wallet.sol @@ -27,4 +27,12 @@ contract Wallet is Ownable { emit Pay(_beneficiary, amount); } + function genericCall(address _contract, bytes memory _encodedABI) + public + returns(bool success, bytes memory returnValue) { + // solhint-disable-next-line avoid-low-level-calls + (success, returnValue) = _contract.call(_encodedABI); + require(success, "call fail"); + } + } diff --git a/test/timelock.js b/test/timelock.js new file mode 100644 index 00000000..878eee86 --- /dev/null +++ b/test/timelock.js @@ -0,0 +1,45 @@ +const helpers = require('./helpers'); + +const Wallet = artifacts.require("./Wallet.sol"); +const TimeLock = artifacts.require("./TimeLock.sol"); +contract('TimeLock', accounts => { + + it("sendEther", async () => { + var wallet = await Wallet.new(); + await wallet.initialize(accounts[0]); + var owner = wallet.address; + var block = await web3.eth.getBlock("latest"); + var releaseTime = block.timestamp + (30*60*60*24); + var timeLock = await TimeLock.new(owner,releaseTime); + assert.equal(await timeLock.owner(), owner); + assert.equal(await timeLock.releaseTime(), releaseTime); + + //send funds to wallet + await web3.eth.sendTransaction({from:accounts[0],to:owner, value: web3.utils.toWei('10', "ether")}); + assert.equal(await web3.eth.getBalance(owner), web3.utils.toWei('10', "ether")); + await wallet.pay(timeLock.address); + await wallet.pay(timeLock.address); + assert.equal(await web3.eth.getBalance(timeLock.address), web3.utils.toWei('10', "ether")); + + var encodedABI = await new web3.eth.Contract(timeLock.abi) + .methods + .withdraw() + .encodeABI(); + + try { + await wallet.genericCall(timeLock.address, encodedABI); + throw 'cannot withdraw before time'; + } catch (error) { + helpers.assertVMException(error); + } + await helpers.increaseTime((30*60*60*24)+1); + try { + await timeLock.withdraw(); + throw 'only Owner can withdraw'; + } catch (error) { + helpers.assertVMException(error); + } + await wallet.genericCall(timeLock.address, encodedABI); + assert.equal(await web3.eth.getBalance(owner), web3.utils.toWei('10', "ether")); + }); +});