Merge pull request #83 from 3esmit/erc20-multisig
wallet factory, and more organizated contracts
This commit is contained in:
commit
af4588dd9b
|
@ -0,0 +1,14 @@
|
|||
pragma solidity ^0.4.15;
|
||||
|
||||
import "./MultiSigStub.sol";
|
||||
|
||||
contract MultiSigFactory {
|
||||
|
||||
event Create(address indexed caller, address createdContract);
|
||||
|
||||
function create(address[] owners, uint256 required) returns (address wallet){
|
||||
wallet = new MultiSigStub(owners, required);
|
||||
Create(msg.sender, wallet);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
pragma solidity ^0.4.15;
|
||||
|
||||
/**
|
||||
* @title MultiSigStub
|
||||
* Contract that delegates calls to a library to build a full MultiSigWallet that is cheap to create.
|
||||
*/
|
||||
contract MultiSigStub {
|
||||
|
||||
function MultiSigStub(address[] _owners, uint256 _required) {
|
||||
//bytes4 sig = bytes4(sha3("Constructor(address[],uint256)"));
|
||||
bytes4 sig = 0xe0c4e63b;
|
||||
uint argarraysize = (2 + _owners.length);
|
||||
uint argsize = (1 + argarraysize) * 32;
|
||||
uint size = 4 + argsize;
|
||||
bytes32 m_data = _malloc(size);
|
||||
|
||||
assembly {
|
||||
mstore(m_data, sig)
|
||||
codecopy(add(m_data, 0x4), sub(codesize, argsize), argsize)
|
||||
}
|
||||
_delegatecall(m_data, size);
|
||||
}
|
||||
|
||||
function()
|
||||
payable
|
||||
{
|
||||
uint size = msg.data.length;
|
||||
bytes32 m_data = _malloc(size);
|
||||
|
||||
assembly {
|
||||
calldatacopy(m_data, 0x0, size)
|
||||
}
|
||||
|
||||
bytes32 m_result = _delegatecall(m_data, size);
|
||||
|
||||
assembly {
|
||||
return(m_result, 0x20)
|
||||
}
|
||||
}
|
||||
|
||||
function _malloc(uint size)
|
||||
private
|
||||
returns(bytes32 m_data)
|
||||
{
|
||||
assembly {
|
||||
m_data := mload(0x40)
|
||||
mstore(0x40, add(m_data, size))
|
||||
}
|
||||
}
|
||||
|
||||
function _delegatecall(bytes32 m_data, uint size)
|
||||
private
|
||||
returns(bytes32 m_result)
|
||||
{
|
||||
address target = 0x370A93cd1DC15875fF02aa0b952D44Bb3dD905E5; //will be replaced by correct value
|
||||
m_result = _malloc(32);
|
||||
bool failed;
|
||||
|
||||
assembly {
|
||||
failed := iszero(delegatecall(sub(gas, 10000), target, m_data, size, m_result, 0x20))
|
||||
}
|
||||
|
||||
assert(!failed);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
pragma solidity ^0.4.11;
|
||||
pragma solidity ^0.4.15;
|
||||
|
||||
contract ERC20 {
|
||||
uint256 public totalSupply;
|
||||
|
@ -13,7 +13,25 @@ contract ERC20 {
|
|||
|
||||
contract MultiSigTokenWallet {
|
||||
|
||||
address constant _walletLibrary = 0x0;
|
||||
address[] public owners;
|
||||
mapping (uint => Transaction) public transactions;
|
||||
mapping (uint => mapping (address => bool)) public confirmations;
|
||||
|
||||
mapping (address => bool) public isOwner;
|
||||
mapping (address => uint) public tokenBalances;
|
||||
mapping (address => address[]) public userList;
|
||||
address[] public tokens;
|
||||
uint public required;
|
||||
uint public transactionCount;
|
||||
uint nonce;
|
||||
|
||||
struct Transaction {
|
||||
address destination;
|
||||
uint value;
|
||||
bytes data;
|
||||
bool executed;
|
||||
}
|
||||
|
||||
uint constant public MAX_OWNER_COUNT = 50;
|
||||
|
||||
event Confirmation(address indexed _sender, uint indexed _transactionId);
|
||||
|
@ -26,27 +44,7 @@ contract MultiSigTokenWallet {
|
|||
event OwnerAddition(address indexed _owner);
|
||||
event OwnerRemoval(address indexed _owner);
|
||||
event RequirementChange(uint _required);
|
||||
event IgnoredToken(address indexed _owner, address _token, bool ignored);
|
||||
|
||||
mapping (uint => Transaction) public transactions;
|
||||
mapping (uint => mapping (address => bool)) public confirmations;
|
||||
mapping (address => bool) public isOwner;
|
||||
address[] public owners;
|
||||
uint public required;
|
||||
uint public transactionCount;
|
||||
|
||||
mapping (address => uint) public tokenBalances;
|
||||
mapping (address => bool) public ignoredTokens;
|
||||
address[] public tokens;
|
||||
uint nonce;
|
||||
|
||||
struct Transaction {
|
||||
address destination;
|
||||
uint value;
|
||||
bytes data;
|
||||
bool executed;
|
||||
}
|
||||
|
||||
|
||||
modifier onlyWallet() {
|
||||
require (msg.sender == address(this));
|
||||
_;
|
||||
|
@ -88,7 +86,7 @@ contract MultiSigTokenWallet {
|
|||
}
|
||||
|
||||
modifier validRequirement(uint ownerCount, uint _required) {
|
||||
require ( ownerCount <= MAX_OWNER_COUNT
|
||||
require (ownerCount <= MAX_OWNER_COUNT
|
||||
&& _required <= ownerCount
|
||||
&& _required != 0
|
||||
&& ownerCount != 0);
|
||||
|
@ -103,16 +101,18 @@ contract MultiSigTokenWallet {
|
|||
Deposit(msg.sender, msg.value);
|
||||
}
|
||||
|
||||
/*
|
||||
* Public functions
|
||||
*/
|
||||
/**
|
||||
* Public functions
|
||||
*
|
||||
**/
|
||||
/// @dev Contract constructor sets initial owners and required number of confirmations.
|
||||
/// @param _owners List of initial owners.
|
||||
/// @param _required Number of required confirmations.
|
||||
function MultiSigTokenWallet(address[] _owners, uint _required)
|
||||
function Constructor(address[] _owners, uint _required)
|
||||
public
|
||||
validRequirement(_owners.length, _required)
|
||||
{
|
||||
require(owners.length == 0 && required == 0);
|
||||
for (uint i=0; i<_owners.length; i++) {
|
||||
require (!isOwner[_owners[i]] && _owners[i] != 0);
|
||||
isOwner[_owners[i]] = true;
|
||||
|
@ -149,7 +149,32 @@ contract MultiSigTokenWallet {
|
|||
_deposited(_from, _amount, _token, _data);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @notice watches for balance in a token contract
|
||||
* @param _tokenAddr the token contract address
|
||||
* @param _data any data
|
||||
**/
|
||||
function watch(address _tokenAddr, bytes _data)
|
||||
ownerExists(msg.sender)
|
||||
{
|
||||
uint oldBal = tokenBalances[_tokenAddr];
|
||||
uint newBal = ERC20(_tokenAddr).balanceOf(this);
|
||||
if(newBal > oldBal){
|
||||
_deposited(0x0, newBal-oldBal, _tokenAddr, _data);
|
||||
}
|
||||
}
|
||||
|
||||
function setMyTokenList(address[] _tokenList)
|
||||
{
|
||||
userList[msg.sender] = _tokenList;
|
||||
}
|
||||
|
||||
function setTokenList(address[] _tokenList)
|
||||
onlyWallet
|
||||
{
|
||||
tokens = _tokenList;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice ERC23 Token fallback
|
||||
* @param _from address incoming token
|
||||
|
@ -188,33 +213,6 @@ contract MultiSigTokenWallet {
|
|||
OwnerAddition(owner);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice watches for balance in a token contract
|
||||
* @param _tokenAddr the token contract address
|
||||
* @param _data any data
|
||||
**/
|
||||
function watch(address _tokenAddr, bytes _data)
|
||||
ownerExists(msg.sender)
|
||||
{
|
||||
uint oldBal = tokenBalances[_tokenAddr];
|
||||
uint newBal = ERC20(_tokenAddr).balanceOf(this);
|
||||
if(newBal > oldBal){
|
||||
_deposited(0x0, newBal-oldBal, _tokenAddr, _data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice ignores a token for sendAll
|
||||
* @param _tokenAddr the token contract address
|
||||
* @param _ignore true if is to ignore, false if not ignore (default)
|
||||
**/
|
||||
function ignoreToken(address _tokenAddr, bool _ignore)
|
||||
ownerExists(msg.sender)
|
||||
{
|
||||
IgnoredToken(msg.sender, _tokenAddr, _ignore);
|
||||
ignoredTokens[_tokenAddr] = _ignore;
|
||||
}
|
||||
|
||||
/// @dev Allows to remove an owner. Transaction has to be sent by wallet.
|
||||
/// @param owner Address of owner.
|
||||
function removeOwner(address owner)
|
||||
|
@ -254,6 +252,24 @@ contract MultiSigTokenWallet {
|
|||
OwnerAddition(newOwner);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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);
|
||||
for(uint i = 0; i < numOwners; i++){
|
||||
removeOwner(_owners[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Allows to change the number of required confirmations. Transaction has to be sent by wallet.
|
||||
/// @param _required Number of required confirmations.
|
||||
function changeRequirement(uint _required)
|
||||
|
@ -321,24 +337,6 @@ contract MultiSigTokenWallet {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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);
|
||||
for(uint i = 0; i < numOwners; i++){
|
||||
removeOwner(_owners[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev withdraw all recognized tokens balances and ether to `_dest`
|
||||
* @param _dest the address of receiver
|
||||
|
@ -361,11 +359,17 @@ contract MultiSigTokenWallet {
|
|||
notNull(_dest)
|
||||
onlyWallet
|
||||
{
|
||||
uint len = tokens.length;
|
||||
address[] memory _tokenList;
|
||||
if(userList[_dest].length > 0){
|
||||
_tokenList = userList[_dest];
|
||||
} else {
|
||||
_tokenList = tokens;
|
||||
}
|
||||
uint len = _tokenList.length;
|
||||
for(uint i = 0;i< len; i++){
|
||||
address _tokenAddr = tokens[i];
|
||||
address _tokenAddr = _tokenList[i];
|
||||
uint _amount = tokenBalances[_tokenAddr];
|
||||
if(_amount > 0 && !ignoredTokens[_tokenAddr]) {
|
||||
if(_amount > 0) {
|
||||
delete tokenBalances[_tokenAddr];
|
||||
ERC20(_tokenAddr).transfer(_dest, _amount);
|
||||
}
|
||||
|
@ -448,172 +452,8 @@ contract MultiSigTokenWallet {
|
|||
}
|
||||
|
||||
/*
|
||||
* Web3 call functions
|
||||
*/
|
||||
/// @dev Returns number of confirmations of a transaction.
|
||||
/// @param transactionId Transaction ID.
|
||||
/// @return Number of confirmations.
|
||||
function getConfirmationCount(uint transactionId)
|
||||
public
|
||||
constant
|
||||
returns (uint count)
|
||||
{
|
||||
for (uint i=0; i<owners.length; i++)
|
||||
if (confirmations[transactionId][owners[i]])
|
||||
count += 1;
|
||||
}
|
||||
|
||||
/// @dev Returns total number of transactions after filters are applied.
|
||||
/// @param pending Include pending transactions.
|
||||
/// @param executed Include executed transactions.
|
||||
/// @return Total number of transactions after filters are applied.
|
||||
function getTransactionCount(bool pending, bool executed)
|
||||
public
|
||||
constant
|
||||
returns (uint count)
|
||||
{
|
||||
for (uint i=0; i<transactionCount; i++)
|
||||
if ( pending && !transactions[i].executed
|
||||
|| executed && transactions[i].executed)
|
||||
count += 1;
|
||||
}
|
||||
|
||||
/// @dev Returns list of owners.
|
||||
/// @return List of owner addresses.
|
||||
function getOwners()
|
||||
public
|
||||
constant
|
||||
returns (address[])
|
||||
{
|
||||
return owners;
|
||||
}
|
||||
|
||||
/// @dev Returns array with owner addresses, which confirmed transaction.
|
||||
/// @param transactionId Transaction ID.
|
||||
/// @return Returns array of owner addresses.
|
||||
function getConfirmations(uint transactionId)
|
||||
public
|
||||
constant
|
||||
returns (address[] _confirmations)
|
||||
{
|
||||
address[] memory confirmationsTemp = new address[](owners.length);
|
||||
uint count = 0;
|
||||
uint i;
|
||||
for (i=0; i<owners.length; i++)
|
||||
if (confirmations[transactionId][owners[i]]) {
|
||||
confirmationsTemp[count] = owners[i];
|
||||
count += 1;
|
||||
}
|
||||
_confirmations = new address[](count);
|
||||
for (i=0; i<count; i++)
|
||||
_confirmations[i] = confirmationsTemp[i];
|
||||
}
|
||||
|
||||
/// @dev Returns list of transaction IDs in defined range.
|
||||
/// @param from Index start position of transaction array.
|
||||
/// @param to Index end position of transaction array.
|
||||
/// @param pending Include pending transactions.
|
||||
/// @param executed Include executed transactions.
|
||||
/// @return Returns array of transaction IDs.
|
||||
function getTransactionIds(uint from, uint to, bool pending, bool executed)
|
||||
public
|
||||
constant
|
||||
returns (uint[] _transactionIds)
|
||||
{
|
||||
uint[] memory transactionIdsTemp = new uint[](transactionCount);
|
||||
uint count = 0;
|
||||
uint i;
|
||||
for (i=0; i<transactionCount; i++)
|
||||
if ( pending && !transactions[i].executed
|
||||
|| executed && transactions[i].executed)
|
||||
{
|
||||
transactionIdsTemp[count] = i;
|
||||
count += 1;
|
||||
}
|
||||
_transactionIds = new uint[](to - from);
|
||||
for (i=from; i<to; i++)
|
||||
_transactionIds[i - from] = transactionIdsTemp[i];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
contract EnhancedMultiSig {
|
||||
|
||||
address constant _walletLibrary = 0xf5f6853e0ebA27074A804358eEdF4E89eFaebc98;
|
||||
uint constant public MAX_OWNER_COUNT = 50;
|
||||
|
||||
event Confirmation(address indexed _sender, uint indexed _transactionId);
|
||||
event Revocation(address indexed _sender, uint indexed _transactionId);
|
||||
event Submission(uint indexed _transactionId);
|
||||
event Execution(uint indexed _transactionId);
|
||||
event ExecutionFailure(uint indexed _transactionId);
|
||||
event Deposit(address indexed _sender, uint _value);
|
||||
event TokenDeposit(address _token, address indexed _sender, uint _value);
|
||||
event OwnerAddition(address indexed _owner);
|
||||
event OwnerRemoval(address indexed _owner);
|
||||
event RequirementChange(uint _required);
|
||||
event IgnoredToken(address indexed _owner, address _token, bool ignored);
|
||||
|
||||
mapping (uint => Transaction) public transactions;
|
||||
mapping (uint => mapping (address => bool)) public confirmations;
|
||||
mapping (address => bool) public isOwner;
|
||||
address[] public owners;
|
||||
uint public required;
|
||||
uint public transactionCount;
|
||||
|
||||
mapping (address => uint) public tokenBalances;
|
||||
mapping (address => bool) public ignoredTokens;
|
||||
address[] public tokens;
|
||||
uint nonce;
|
||||
|
||||
struct Transaction {
|
||||
address destination;
|
||||
uint value;
|
||||
bytes data;
|
||||
bool executed;
|
||||
}
|
||||
|
||||
|
||||
// WALLET CONSTRUCTOR
|
||||
function EnhancedMultiSig(address[] _owners, uint _required) {
|
||||
require(_required > 0);
|
||||
uint len = _owners.length;
|
||||
require(len <= MAX_OWNER_COUNT);
|
||||
require(len >= _required);
|
||||
for (uint i=0; i < len; i++) {
|
||||
require (!isOwner[_owners[i]] && _owners[i] != 0);
|
||||
isOwner[_owners[i]] = true;
|
||||
}
|
||||
owners = _owners;
|
||||
required = _required;
|
||||
}
|
||||
|
||||
/// @dev Fallback function allows to deposit ether or other functions in library.
|
||||
function()
|
||||
payable
|
||||
{
|
||||
_walletLibrary.delegatecall(msg.data);
|
||||
}
|
||||
|
||||
/// @dev Returns the confirmation status of a transaction.
|
||||
/// @param transactionId Transaction ID.
|
||||
/// @return Confirmation status.
|
||||
function isConfirmed(uint transactionId)
|
||||
public
|
||||
constant
|
||||
returns (bool)
|
||||
{
|
||||
uint count = 0;
|
||||
for (uint i=0; i<owners.length; i++) {
|
||||
if (confirmations[transactionId][owners[i]])
|
||||
count += 1;
|
||||
if (count == required)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Web3 call functions
|
||||
*/
|
||||
* Web3 call functions
|
||||
*/
|
||||
/// @dev Returns number of confirmations of a transaction.
|
||||
/// @param transactionId Transaction ID.
|
||||
/// @return Number of confirmations.
|
||||
|
@ -652,6 +492,16 @@ contract EnhancedMultiSig {
|
|||
return owners;
|
||||
}
|
||||
|
||||
/// @dev Returns list of tokens.
|
||||
/// @return List of token addresses.
|
||||
function getTokenList()
|
||||
public
|
||||
constant
|
||||
returns (address[])
|
||||
{
|
||||
return tokens;
|
||||
}
|
||||
|
||||
/// @dev Returns array with owner addresses, which confirmed transaction.
|
||||
/// @param transactionId Transaction ID.
|
||||
/// @return Returns array of owner addresses.
|
|
@ -6,7 +6,8 @@
|
|||
[clojure.string :refer [join]]
|
||||
[clojure.tools.logging :as log]
|
||||
[clojure.string :as str]
|
||||
[pandect.core :as pandect]))
|
||||
[pandect.core :as pandect]
|
||||
[commiteth.eth.multisig-wallet :as multisig]))
|
||||
|
||||
(defn eth-rpc-url [] (env :eth-rpc-url "http://localhost:8545"))
|
||||
(defn eth-account [] (:eth-account env))
|
||||
|
@ -95,13 +96,7 @@
|
|||
|
||||
(defn deploy-contract
|
||||
[owner]
|
||||
(let [contract-code (-> "contracts/wallet.data" io/resource slurp)
|
||||
owner1 (format-param (eth-account))
|
||||
owner2 (format-param owner)
|
||||
data (str contract-code owner1 owner2)
|
||||
value (format "0x%x" 0)]
|
||||
(send-transaction (eth-account) nil value {:gas "0x80000"
|
||||
:data data})))
|
||||
(multisig/create-new (eth-account) owner 2))
|
||||
|
||||
(defn format-call-params
|
||||
[method-id & params]
|
||||
|
|
|
@ -2,12 +2,26 @@
|
|||
(:require [commiteth.eth.core :as eth]
|
||||
[clojure.tools.logging :as log]))
|
||||
|
||||
(defonce confirmation-topic "0xc0ba8fe4b176c1714197d43b9cc6bcf797a4a7461c5fe8d0ef6e184ae7601e51")
|
||||
|
||||
(defonce method-ids
|
||||
{:submit-transaction (eth/sig->method-id "submitTransaction(address,uint256,bytes)")
|
||||
:withdraw-everything (eth/sig->method-id "withdrawEverything(address)")})
|
||||
|
||||
(defonce factory "0xbcBc5b8cE5c76Ed477433636926f76897401f838")
|
||||
|
||||
(defonce factory-topic "0x96b5b9b8a7193304150caccf9b80d150675fa3d6af57761d8d8ef1d6f9a1a909")
|
||||
|
||||
(defonce confirmation-topic "0xc0ba8fe4b176c1714197d43b9cc6bcf797a4a7461c5fe8d0ef6e184ae7601e51")
|
||||
|
||||
(defn create-new
|
||||
[owner1 owner2 required]
|
||||
(eth/execute (eth/eth-account)
|
||||
factory
|
||||
"0xf8f73808"
|
||||
0x40
|
||||
0x2
|
||||
required
|
||||
owner1
|
||||
owner2))
|
||||
|
||||
(defn execute
|
||||
[contract to value]
|
||||
|
@ -32,6 +46,18 @@
|
|||
(when confirmation-data
|
||||
(subs confirmation-data 2 66))))
|
||||
|
||||
(defn find-factory-hash
|
||||
[receipt]
|
||||
(let [logs (:logs receipt)
|
||||
has-factory-event #(some (fn [topic] (= topic
|
||||
factory-topic))
|
||||
(:topics %))
|
||||
factory-event (first (filter has-factory-event logs))
|
||||
factory-data (:data factory-event)]
|
||||
(when factory-data
|
||||
(subs factory-data 2 66))))
|
||||
|
||||
|
||||
(defn send-all
|
||||
[contract to]
|
||||
(log/debug "multisig.send-all(contract, to)" contract to)
|
||||
|
@ -46,3 +72,22 @@
|
|||
"0x60"
|
||||
"0x24"
|
||||
params)))
|
||||
|
||||
|
||||
(defn watch-token
|
||||
[contract token]
|
||||
(log/debug "multisig.watch-token(contract, token)" contract token)
|
||||
(eth/execute (eth/eth-account)
|
||||
contract
|
||||
"0xf375c07a"
|
||||
token
|
||||
0))
|
||||
|
||||
(defn token-balance
|
||||
[contract token]
|
||||
(eth/call contract "0x523fba7f" token))
|
||||
|
||||
(defn tokens-list
|
||||
[contract]
|
||||
(eth/call contract "0x273cbaa0"))
|
||||
|
||||
|
|
Loading…
Reference in New Issue