Skip to content

Commit c0e07a8

Browse files
author
Radek Ostrowski
committed
init
0 parents  commit c0e07a8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+19021
-0
lines changed

.gitignore

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
build
2+
node_modules
3+
.DS_Store
4+
*.sublime-project
5+
*.sublime-workspace

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2017 Radek Ostrowski
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# Time Locked Wallet
2+
3+
4+
5+
## Setup Environment
6+
Firstly, install [Node](https://nodejs.org/en/) and [Git](https://git-scm.com/) on your machine.
7+
Once it's done install Truffle with:
8+
```
9+
npm install -g truffle
10+
```
11+
12+
Now, get the code:
13+
```
14+
git clone https://github.com/radek1st/time-locked-wallets
15+
cd time-locked-wallet-contract
16+
```
17+
18+
## Contract Development
19+
20+
Start Truffle Console and Compile/Deploy/Test
21+
22+
To get started quickly run Truffle with inbuilt blockchain:
23+
```
24+
truffle develop
25+
```
26+
Alternatively, install and start [Ganache](http://truffleframework.com/ganache/)
27+
which comes with a nice UI to the local blockchain and then connect to it with:
28+
```
29+
truffle --network ganache console
30+
```
31+
32+
To compile the contracts run:
33+
```
34+
> compile
35+
```
36+
To deploy the contracts run:
37+
```
38+
> migrate
39+
```
40+
To run the tests:
41+
```
42+
> test
43+
```
44+
45+
## Contract Interaction with ÐApp
46+
47+
### Install and Configure MetaMask extension
48+
49+
Install [MetaMask](https://metamask.io/) in your Chrome browser.
50+
51+
52+
Import the wallet seed from the testnet (should be something like):
53+
```
54+
candy maple cake sugar pudding cream honey rich smooth crumble sweet treat
55+
```
56+
57+
Select Network:
58+
* for default use `http://localhost:9545`
59+
* for Ganache use `http://localhost:7545`
60+
61+
### Run ÐApp
62+
To see the Time Locked Wallet in action, start the web server locally:
63+
```
64+
npm run dev
65+
```
66+
It should automatically open the browser at `http://localhost:XXXX`
67+
68+
69+
You can then interact with the contract form the web UI and switch the accounts in MetaMask.
70+
71+
72+
73+
### Other ideas
74+
75+
* multisig wallet/will -- children&partner must agree,
76+
* give a signed message to withdraw the money, doesn't have to be address, could be just the signed message
64.2 KB
Loading
61.7 KB
Loading
99.7 KB
Loading
62.9 KB
Loading
167 KB
Loading
Loading
Loading

blog/screenshots/wallet-creation.png

388 KB
Loading
Loading

blog/screenshots/wallet-open.png

181 KB
Loading
Loading
Loading
379 KB
Loading

blog/screenshots/withdraw-ether.png

381 KB
Loading

blog/screenshots/withdraw-tokens.png

377 KB
Loading

bs-config.json

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"server": {
3+
"baseDir": ["./src", "./build/contracts"]
4+
}
5+
}

contracts/ERC20.sol

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
pragma solidity ^0.4.18;
2+
3+
4+
/**
5+
* @title ERC20
6+
* @dev see https://github.com/ethereum/EIPs/issues/20
7+
*/
8+
contract ERC20 {
9+
uint256 public totalSupply;
10+
11+
function balanceOf(address who) public view returns (uint256);
12+
function transfer(address to, uint256 value) public returns (bool);
13+
function allowance(address owner, address spender) public view returns (uint256);
14+
function transferFrom(address from, address to, uint256 value) public returns (bool);
15+
function approve(address spender, uint256 value) public returns (bool);
16+
17+
event Approval(address indexed owner, address indexed spender, uint256 value);
18+
event Transfer(address indexed from, address indexed to, uint256 value);
19+
}

contracts/Migrations.sol

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
pragma solidity ^0.4.2;
2+
3+
contract Migrations {
4+
address public owner;
5+
uint public last_completed_migration;
6+
7+
modifier restricted() {
8+
if (msg.sender == owner) _;
9+
}
10+
11+
function Migrations() public {
12+
owner = msg.sender;
13+
}
14+
15+
function setCompleted(uint completed) restricted public {
16+
last_completed_migration = completed;
17+
}
18+
19+
function upgrade(address new_address) restricted public {
20+
Migrations upgraded = Migrations(new_address);
21+
upgraded.setCompleted(last_completed_migration);
22+
}
23+
}

contracts/SafeMath.sol

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
pragma solidity ^0.4.18;
2+
3+
4+
/**
5+
* @title SafeMath
6+
* @dev Math operations with safety checks that throw on error
7+
*/
8+
library SafeMath {
9+
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
10+
if (a == 0) {
11+
return 0;
12+
}
13+
uint256 c = a * b;
14+
assert(c / a == b);
15+
return c;
16+
}
17+
18+
function div(uint256 a, uint256 b) internal pure returns (uint256) {
19+
// assert(b > 0); // Solidity automatically throws when dividing by 0
20+
uint256 c = a / b;
21+
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
22+
return c;
23+
}
24+
25+
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
26+
assert(b <= a);
27+
return a - b;
28+
}
29+
30+
function add(uint256 a, uint256 b) internal pure returns (uint256) {
31+
uint256 c = a + b;
32+
assert(c >= a);
33+
return c;
34+
}
35+
}

contracts/TimeLockedWallet.sol

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
pragma solidity ^0.4.18;
2+
3+
import "./ERC20.sol";
4+
5+
contract TimeLockedWallet {
6+
7+
address public creator;
8+
address public owner;
9+
uint public unlockDate;
10+
uint public createdAt;
11+
12+
modifier onlyOwner {
13+
require(msg.sender == owner);
14+
_;
15+
}
16+
17+
function TimeLockedWallet(
18+
address _creator,
19+
address _owner,
20+
uint _unlockDate
21+
) public {
22+
creator = _creator;
23+
owner = _owner;
24+
unlockDate = _unlockDate;
25+
createdAt = now;
26+
}
27+
28+
// keep all the ether sent to this address
29+
function() payable public {
30+
Received(msg.sender, msg.value);
31+
}
32+
33+
// callable by owner only, after specified time
34+
function withdraw() onlyOwner public {
35+
require(now >= unlockDate);
36+
//now send all the balance
37+
msg.sender.transfer(this.balance);
38+
Withdrew(msg.sender, this.balance);
39+
}
40+
41+
// callable by owner only, after specified time, only for Tokens implementing ERC20
42+
function withdrawTokens(address _tokenContract) onlyOwner public {
43+
require(now >= unlockDate);
44+
ERC20 token = ERC20(_tokenContract);
45+
//now send all the token balance
46+
uint tokenBalance = token.balanceOf(this);
47+
token.transfer(owner, tokenBalance);
48+
WithdrewTokens(_tokenContract, msg.sender, tokenBalance);
49+
}
50+
51+
function info() public view returns(address, address, uint, uint, uint) {
52+
return (creator, owner, unlockDate, createdAt, this.balance);
53+
}
54+
55+
event Received(address from, uint amount);
56+
event Withdrew(address to, uint amount);
57+
event WithdrewTokens(address tokenContract, address to, uint amount);
58+
}

contracts/TimeLockedWalletFactory.sol

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
pragma solidity ^0.4.18;
2+
3+
import "./TimeLockedWallet.sol";
4+
5+
contract TimeLockedWalletFactory {
6+
7+
mapping(address => address[]) wallets;
8+
9+
function getWallets(address _user)
10+
public
11+
view
12+
returns(address[])
13+
{
14+
return wallets[_user];
15+
}
16+
17+
function newTimeLockedWallet(address _owner, uint _unlockDate)
18+
payable
19+
public
20+
returns(address wallet)
21+
{
22+
// Create new wallet.
23+
wallet = new TimeLockedWallet(msg.sender, _owner, _unlockDate);
24+
25+
// Add wallet to sender's wallets.
26+
wallets[msg.sender].push(wallet);
27+
28+
// If owner is the same as sender then add wallet to sender's wallets too.
29+
if(msg.sender != _owner){
30+
wallets[_owner].push(wallet);
31+
}
32+
33+
// Send ether from this transaction to the created contract.
34+
wallet.transfer(msg.value);
35+
36+
// Emit event.
37+
Created(wallet, msg.sender, _owner, now, _unlockDate, msg.value);
38+
}
39+
40+
// Prevents accidental sending of ether to the factory
41+
function () public {
42+
revert();
43+
}
44+
45+
event Created(address wallet, address from, address to, uint createdAt, uint unlockDate, uint amount);
46+
}

contracts/ToptalToken.sol

+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
pragma solidity ^0.4.18;
2+
3+
import './SafeMath.sol';
4+
import './ERC20.sol';
5+
6+
/**
7+
* @title Toptal token
8+
*/
9+
10+
contract ToptalToken is ERC20 {
11+
using SafeMath for uint256;
12+
13+
mapping(address => uint256) balances;
14+
mapping (address => mapping (address => uint256)) internal allowed;
15+
16+
string public name = "Toptal Token";
17+
string public symbol = "TTT";
18+
uint8 public decimals = 6;
19+
uint256 public totalSupply = 1000000000000;
20+
21+
function ToptalToken() public {
22+
balances[msg.sender] = totalSupply;
23+
}
24+
25+
/**
26+
* @dev Gets the balance of the specified address.
27+
* @param _owner The address to query the the balance of.
28+
* @return An uint256 representing the amount owned by the passed address.
29+
*/
30+
function balanceOf(address _owner) public view returns (uint256 balance) {
31+
return balances[_owner];
32+
}
33+
34+
/**
35+
* @dev transfer token for a specified address
36+
* @param _to The address to transfer to.
37+
* @param _value The amount to be transferred.
38+
*/
39+
function transfer(address _to, uint256 _value) public returns (bool) {
40+
require(_to != address(0));
41+
require(_value <= balances[msg.sender]);
42+
43+
// SafeMath.sub will throw if there is not enough balance.
44+
balances[msg.sender] = balances[msg.sender].sub(_value);
45+
balances[_to] = balances[_to].add(_value);
46+
Transfer(msg.sender, _to, _value);
47+
return true;
48+
}
49+
50+
/**
51+
* @dev Transfer tokens from one address to another
52+
* @param _from address The address which you want to send tokens from
53+
* @param _to address The address which you want to transfer to
54+
* @param _value uint256 the amount of tokens to be transferred
55+
*/
56+
function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
57+
require(_to != address(0));
58+
require(_value <= balances[_from]);
59+
require(_value <= allowed[_from][msg.sender]);
60+
61+
balances[_from] = balances[_from].sub(_value);
62+
balances[_to] = balances[_to].add(_value);
63+
allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
64+
Transfer(_from, _to, _value);
65+
return true;
66+
}
67+
68+
/**
69+
* @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
70+
*
71+
* Beware that changing an allowance with this method brings the risk that someone may use both the old
72+
* and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
73+
* race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
74+
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
75+
* @param _spender The address which will spend the funds.
76+
* @param _value The amount of tokens to be spent.
77+
*/
78+
function approve(address _spender, uint256 _value) public returns (bool) {
79+
allowed[msg.sender][_spender] = _value;
80+
Approval(msg.sender, _spender, _value);
81+
return true;
82+
}
83+
84+
/**
85+
* @dev Function to check the amount of tokens that an owner allowed to a spender.
86+
* @param _owner address The address which owns the funds.
87+
* @param _spender address The address which will spend the funds.
88+
* @return A uint256 specifying the amount of tokens still available for the spender.
89+
*/
90+
function allowance(address _owner, address _spender) public view returns (uint256) {
91+
return allowed[_owner][_spender];
92+
}
93+
94+
event Transfer(address indexed from, address indexed to, uint256 value);
95+
event Approval(address indexed owner, address indexed spender, uint256 value);
96+
}
97+

0 commit comments

Comments
 (0)