2017-08-16 09:00:24 +00:00
|
|
|
pragma solidity ^0.4.15;
|
2017-08-02 03:10:54 +00:00
|
|
|
|
2017-11-08 19:36:15 +00:00
|
|
|
import "./MultiSigWallet.sol";
|
2017-08-20 18:29:37 +00:00
|
|
|
import "./ERC20.sol";
|
2017-08-02 03:10:54 +00:00
|
|
|
|
2017-11-08 19:36:15 +00:00
|
|
|
contract MultiSigTokenWallet is MultiSigWallet {
|
2017-08-01 08:37:25 +00:00
|
|
|
|
2017-08-20 01:05:58 +00:00
|
|
|
address[] public tokens;
|
2017-08-01 08:37:25 +00:00
|
|
|
mapping (address => uint) public tokenBalances;
|
2017-08-17 13:06:14 +00:00
|
|
|
mapping (address => address[]) public userList;
|
2017-08-20 01:05:58 +00:00
|
|
|
uint public nonce;
|
2017-08-02 03:10:54 +00:00
|
|
|
|
2017-08-08 07:21:17 +00:00
|
|
|
event TokenDeposit(address _token, address indexed _sender, uint _value);
|
2017-08-01 08:37:25 +00:00
|
|
|
|
2017-08-16 09:00:24 +00:00
|
|
|
/**
|
2017-08-17 13:06:14 +00:00
|
|
|
* Public functions
|
|
|
|
*
|
|
|
|
**/
|
2017-08-01 08:37:25 +00:00
|
|
|
/**
|
|
|
|
* @notice deposit a ERC20 token. The amount of deposit is the allowance set to this contract.
|
|
|
|
* @param _token the token contract address
|
|
|
|
* @param _data might be used by child implementations
|
|
|
|
**/
|
2017-08-02 03:10:54 +00:00
|
|
|
function depositToken(address _token, bytes _data)
|
|
|
|
public
|
|
|
|
{
|
2017-08-01 08:37:25 +00:00
|
|
|
address sender = msg.sender;
|
|
|
|
uint amount = ERC20(_token).allowance(sender, this);
|
|
|
|
deposit(sender, amount, _token, _data);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @notice deposit a ERC20 token. The amount of deposit is the allowance set to this contract.
|
|
|
|
* @param _token the token contract address
|
|
|
|
* @param _data might be used by child implementations
|
|
|
|
**/
|
2017-08-02 03:10:54 +00:00
|
|
|
function deposit(address _from, uint256 _amount, address _token, bytes _data)
|
|
|
|
public
|
|
|
|
{
|
2017-08-20 18:29:37 +00:00
|
|
|
if (_from == address(this))
|
|
|
|
return;
|
2017-08-01 08:37:25 +00:00
|
|
|
uint _nonce = nonce;
|
2017-08-20 18:29:37 +00:00
|
|
|
bool result = ERC20(_token).transferFrom(_from, this, _amount);
|
|
|
|
assert(result);
|
|
|
|
//ERC23 not executed _deposited tokenFallback by
|
|
|
|
if (nonce == _nonce) {
|
2017-08-01 08:37:25 +00:00
|
|
|
_deposited(_from, _amount, _token, _data);
|
|
|
|
}
|
|
|
|
}
|
2017-08-17 13:06:14 +00:00
|
|
|
/**
|
|
|
|
* @notice watches for balance in a token contract
|
|
|
|
* @param _tokenAddr the token contract address
|
|
|
|
**/
|
2017-08-20 18:29:37 +00:00
|
|
|
function watch(address _tokenAddr)
|
2017-08-17 13:06:14 +00:00
|
|
|
ownerExists(msg.sender)
|
|
|
|
{
|
|
|
|
uint oldBal = tokenBalances[_tokenAddr];
|
|
|
|
uint newBal = ERC20(_tokenAddr).balanceOf(this);
|
2017-08-20 18:29:37 +00:00
|
|
|
if (newBal > oldBal) {
|
|
|
|
_deposited(0x0, newBal-oldBal, _tokenAddr, new bytes(0));
|
2017-08-17 13:06:14 +00:00
|
|
|
}
|
|
|
|
}
|
2017-08-01 08:37:25 +00:00
|
|
|
|
2017-08-20 18:29:37 +00:00
|
|
|
function setMyTokenList(address[] _tokenList)
|
|
|
|
public
|
2017-08-17 13:06:14 +00:00
|
|
|
{
|
|
|
|
userList[msg.sender] = _tokenList;
|
|
|
|
}
|
|
|
|
|
|
|
|
function setTokenList(address[] _tokenList)
|
|
|
|
onlyWallet
|
|
|
|
{
|
|
|
|
tokens = _tokenList;
|
|
|
|
}
|
|
|
|
|
2017-08-01 08:37:25 +00:00
|
|
|
/**
|
|
|
|
* @notice ERC23 Token fallback
|
|
|
|
* @param _from address incoming token
|
|
|
|
* @param _amount incoming amount
|
|
|
|
**/
|
2017-08-02 03:10:54 +00:00
|
|
|
function tokenFallback(address _from, uint _amount, bytes _data)
|
|
|
|
public
|
|
|
|
{
|
2017-08-01 08:37:25 +00:00
|
|
|
_deposited(_from, _amount, msg.sender, _data);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @notice Called MiniMeToken approvesAndCall to this contract, calls deposit.
|
|
|
|
* @param _from address incoming token
|
|
|
|
* @param _amount incoming amount
|
|
|
|
* @param _token the token contract address
|
|
|
|
* @param _data (might be used by child classes)
|
|
|
|
*/
|
2017-08-20 18:29:37 +00:00
|
|
|
function receiveApproval(address _from, uint256 _amount, address _token, bytes _data) {
|
2017-08-01 08:37:25 +00:00
|
|
|
deposit(_from, _amount, _token, _data);
|
|
|
|
}
|
2017-08-02 03:10:54 +00:00
|
|
|
|
2017-08-17 13:06:14 +00:00
|
|
|
/**
|
|
|
|
* @dev gives full ownership of this wallet to `_dest` removing older owners from wallet
|
|
|
|
* @param _dest the address of new controller
|
|
|
|
**/
|
|
|
|
function releaseWallet(address _dest)
|
|
|
|
public
|
|
|
|
notNull(_dest)
|
|
|
|
ownerDoesNotExist(_dest)
|
|
|
|
onlyWallet
|
|
|
|
{
|
|
|
|
address[] memory _owners = owners;
|
|
|
|
uint numOwners = _owners.length;
|
|
|
|
addOwner(_dest);
|
2017-08-20 18:29:37 +00:00
|
|
|
for (uint i = 0; i < numOwners; i++) {
|
2017-08-17 13:06:14 +00:00
|
|
|
removeOwner(_owners[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-02 03:10:54 +00:00
|
|
|
/**
|
|
|
|
* @dev withdraw all recognized tokens balances and ether to `_dest`
|
|
|
|
* @param _dest the address of receiver
|
|
|
|
**/
|
|
|
|
function withdrawEverything(address _dest)
|
|
|
|
public
|
|
|
|
notNull(_dest)
|
|
|
|
onlyWallet
|
|
|
|
{
|
|
|
|
withdrawAllTokens(_dest);
|
|
|
|
_dest.transfer(this.balance);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dev withdraw all recognized tokens balances to `_dest`
|
|
|
|
* @param _dest the address of receiver
|
|
|
|
**/
|
|
|
|
function withdrawAllTokens(address _dest)
|
|
|
|
public
|
|
|
|
notNull(_dest)
|
|
|
|
onlyWallet
|
|
|
|
{
|
2017-08-17 13:06:14 +00:00
|
|
|
address[] memory _tokenList;
|
2017-08-20 18:29:37 +00:00
|
|
|
if (userList[_dest].length > 0) {
|
2017-08-17 13:06:14 +00:00
|
|
|
_tokenList = userList[_dest];
|
|
|
|
} else {
|
|
|
|
_tokenList = tokens;
|
|
|
|
}
|
|
|
|
uint len = _tokenList.length;
|
2017-08-20 18:29:37 +00:00
|
|
|
for (uint i = 0;i < len; i++) {
|
2017-08-17 13:06:14 +00:00
|
|
|
address _tokenAddr = _tokenList[i];
|
2017-08-01 08:37:25 +00:00
|
|
|
uint _amount = tokenBalances[_tokenAddr];
|
2017-08-20 18:29:37 +00:00
|
|
|
if (_amount > 0) {
|
2017-08-01 08:37:25 +00:00
|
|
|
delete tokenBalances[_tokenAddr];
|
2017-08-02 03:10:54 +00:00
|
|
|
ERC20(_tokenAddr).transfer(_dest, _amount);
|
2017-08-01 08:37:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-08-02 03:10:54 +00:00
|
|
|
|
2017-08-01 08:37:25 +00:00
|
|
|
/**
|
2017-08-02 03:10:54 +00:00
|
|
|
* @dev withdraw `_tokenAddr` `_amount` to `_dest`
|
|
|
|
* @param _tokenAddr the address of the token
|
|
|
|
* @param _dest the address of receiver
|
|
|
|
* @param _amount the number of tokens to send
|
2017-08-01 08:37:25 +00:00
|
|
|
**/
|
2017-08-02 03:10:54 +00:00
|
|
|
function withdrawToken(address _tokenAddr, address _dest, uint _amount)
|
|
|
|
public
|
|
|
|
notNull(_dest)
|
|
|
|
onlyWallet
|
|
|
|
{
|
|
|
|
require(_amount > 0);
|
|
|
|
uint _balance = tokenBalances[_tokenAddr];
|
|
|
|
require(_amount <= _balance);
|
|
|
|
tokenBalances[_tokenAddr] = _balance - _amount;
|
2017-08-20 18:29:37 +00:00
|
|
|
bool result = ERC20(_tokenAddr).transfer(_dest, _amount);
|
|
|
|
assert(result);
|
2017-08-01 08:37:25 +00:00
|
|
|
}
|
2017-08-02 03:10:54 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @dev register the deposit
|
|
|
|
**/
|
2017-08-20 18:29:37 +00:00
|
|
|
function _deposited(address _from, uint _amount, address _tokenAddr, bytes)
|
2017-08-02 03:10:54 +00:00
|
|
|
internal
|
|
|
|
{
|
|
|
|
TokenDeposit(_tokenAddr,_from,_amount);
|
|
|
|
nonce++;
|
2017-08-20 18:29:37 +00:00
|
|
|
if (tokenBalances[_tokenAddr] == 0) {
|
2017-08-02 03:10:54 +00:00
|
|
|
tokens.push(_tokenAddr);
|
|
|
|
tokenBalances[_tokenAddr] = ERC20(_tokenAddr).balanceOf(this);
|
2017-08-20 18:29:37 +00:00
|
|
|
} else {
|
2017-08-02 03:10:54 +00:00
|
|
|
tokenBalances[_tokenAddr] += _amount;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-01 08:37:25 +00:00
|
|
|
/*
|
2017-08-17 13:06:14 +00:00
|
|
|
* Web3 call functions
|
|
|
|
*/
|
|
|
|
/// @dev Returns list of tokens.
|
|
|
|
/// @return List of token addresses.
|
|
|
|
function getTokenList()
|
|
|
|
public
|
|
|
|
constant
|
|
|
|
returns (address[])
|
|
|
|
{
|
|
|
|
return tokens;
|
|
|
|
}
|
|
|
|
|
2017-08-16 09:00:24 +00:00
|
|
|
}
|