Contract Name:
AccountFactory
Contract Source Code:
File 1 of 1 : AccountFactory
pragma solidity 0.4.24;
contract DSMath {
function add(uint x, uint y) internal pure returns (uint z) {
require((z = x + y) >= x);
}
function sub(uint x, uint y) internal pure returns (uint z) {
require((z = x - y) <= x);
}
function mul(uint x, uint y) internal pure returns (uint z) {
require(y == 0 || (z = x * y) / y == x);
}
// custom : not in original DSMath, putting it here for consistency, copied from SafeMath
function div(uint x, uint y) internal pure returns (uint z) {
z = x / y;
}
function min(uint x, uint y) internal pure returns (uint z) {
return x <= y ? x : y;
}
function max(uint x, uint y) internal pure returns (uint z) {
return x >= y ? x : y;
}
function imin(int x, int y) internal pure returns (int z) {
return x <= y ? x : y;
}
function imax(int x, int y) internal pure returns (int z) {
return x >= y ? x : y;
}
uint constant WAD = 10 ** 18;
uint constant RAY = 10 ** 27;
function wmul(uint x, uint y) internal pure returns (uint z) {
z = add(mul(x, y), WAD / 2) / WAD;
}
function rmul(uint x, uint y) internal pure returns (uint z) {
z = add(mul(x, y), RAY / 2) / RAY;
}
function wdiv(uint x, uint y) internal pure returns (uint z) {
z = add(mul(x, WAD), y / 2) / y;
}
function rdiv(uint x, uint y) internal pure returns (uint z) {
z = add(mul(x, RAY), y / 2) / y;
}
// This famous algorithm is called "exponentiation by squaring"
// and calculates x^n with x as fixed-point and n as regular unsigned.
//
// It's O(log n), instead of O(n) for naive repeated multiplication.
//
// These facts are why it works:
//
// If n is even, then x^n = (x^2)^(n/2).
// If n is odd, then x^n = x * x^(n-1),
// and applying the equation for even x gives
// x^n = x * (x^2)^((n-1) / 2).
//
// Also, EVM division is flooring and
// floor[(n-1) / 2] = floor[n / 2].
//
function rpow(uint x, uint n) internal pure returns (uint z) {
z = n % 2 != 0 ? x : RAY;
for (n /= 2; n != 0; n /= 2) {
x = rmul(x, x);
if (n % 2 != 0) {
z = rmul(z, x);
}
}
}
}
contract ErrorUtils {
event LogError(string methodSig, string errMsg);
event LogErrorWithHintBytes32(bytes32 indexed bytes32Value, string methodSig, string errMsg);
event LogErrorWithHintAddress(address indexed addressValue, string methodSig, string errMsg);
}
contract Proxy {
address masterCopy;
constructor(address _masterCopy)
public
{
require(_masterCopy != 0, "Invalid master copy address provided");
masterCopy = _masterCopy;
}
function ()
external
payable
{
// solium-disable-next-line security/no-inline-assembly
assembly {
let masterCopy := and(sload(0), 0xffffffffffffffffffffffffffffffffffffffff)
calldatacopy(0, 0, calldatasize())
let success := delegatecall(gas, masterCopy, 0, calldatasize(), 0, 0)
returndatacopy(0, 0, returndatasize())
if eq(success, 0) { revert(0, returndatasize()) }
return(0, returndatasize())
}
}
function implementation()
public
view
returns (address)
{
return masterCopy;
}
function proxyType()
public
pure
returns (uint256)
{
return 2;
}
}
contract DSAuthority {
function canCall(address src, address dst, bytes4 sig) public view returns (bool);
}
contract DSAuthEvents {
event LogSetAuthority (address indexed authority);
event LogSetOwner (address indexed owner);
}
contract DSAuth is DSAuthEvents {
DSAuthority public authority;
address public owner;
constructor() public {
owner = msg.sender;
emit LogSetOwner(msg.sender);
}
function setOwner(address owner_)
public
auth
{
owner = owner_;
emit LogSetOwner(owner);
}
function setAuthority(DSAuthority authority_)
public
auth
{
authority = authority_;
emit LogSetAuthority(authority);
}
modifier auth {
require(isAuthorized(msg.sender, msg.sig), "DSAuth::_ SENDER_NOT_AUTHORIZED");
_;
}
function isAuthorized(address src, bytes4 sig) internal view returns (bool) {
if (src == address(this)) {
return true;
} else if (src == owner) {
return true;
} else if (authority == DSAuthority(0)) {
return false;
} else {
return authority.canCall(src, this, sig);
}
}
}
contract DSNote {
event LogNote(
bytes4 indexed sig,
address indexed guy,
bytes32 indexed foo,
bytes32 indexed bar,
uint wad,
bytes fax
) anonymous;
modifier note {
bytes32 foo;
bytes32 bar;
assembly {
foo := calldataload(4)
bar := calldataload(36)
}
emit LogNote(msg.sig, msg.sender, foo, bar, msg.value, msg.data);
_;
}
}
interface ERC20 {
function name() public view returns(string);
function symbol() public view returns(string);
function decimals() public view returns(uint8);
function totalSupply() public view returns (uint);
function balanceOf(address tokenOwner) public view returns (uint balance);
function allowance(address tokenOwner, address spender) public view returns (uint remaining);
function transfer(address to, uint tokens) public returns (bool success);
function approve(address spender, uint tokens) public returns (bool success);
function transferFrom(address from, address to, uint tokens) public returns (bool success);
event Transfer(address indexed from, address indexed to, uint tokens);
event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
}
contract MasterCopy {
address masterCopy;
function changeMasterCopy(address _masterCopy)
public
{
require(_masterCopy != 0, "Invalid master copy address provided");
masterCopy = _masterCopy;
}
}
contract Utils {
modifier addressValid(address _address) {
require(_address != address(0), "Utils::_ INVALID_ADDRESS");
_;
}
}
contract WETH9 {
string public name = "Wrapped Ether";
string public symbol = "WETH";
uint8 public decimals = 18;
event Approval(address indexed _owner, address indexed _spender, uint _value);
event Transfer(address indexed _from, address indexed _to, uint _value);
event Deposit(address indexed _owner, uint _value);
event Withdrawal(address indexed _owner, uint _value);
mapping (address => uint) public balanceOf;
mapping (address => mapping (address => uint)) public allowance;
function() public payable {
deposit();
}
function deposit() public payable {
balanceOf[msg.sender] += msg.value;
Deposit(msg.sender, msg.value);
}
function withdraw(uint wad) public {
require(balanceOf[msg.sender] >= wad);
balanceOf[msg.sender] -= wad;
msg.sender.transfer(wad);
Withdrawal(msg.sender, wad);
}
function totalSupply() public view returns (uint) {
return this.balance;
}
function approve(address guy, uint wad) public returns (bool) {
allowance[msg.sender][guy] = wad;
Approval(msg.sender, guy, wad);
return true;
}
function transfer(address dst, uint wad) public returns (bool) {
return transferFrom(msg.sender, dst, wad);
}
function transferFrom(address src, address dst, uint wad)
public
returns (bool)
{
require(balanceOf[src] >= wad);
if (src != msg.sender && allowance[src][msg.sender] != uint(-1)) {
require(allowance[src][msg.sender] >= wad);
allowance[src][msg.sender] -= wad;
}
balanceOf[src] -= wad;
balanceOf[dst] += wad;
Transfer(src, dst, wad);
return true;
}
}
contract DSThing is DSNote, DSAuth, DSMath {
function S(string s) internal pure returns (bytes4) {
return bytes4(keccak256(s));
}
}
library ECRecovery {
function recover(bytes32 _hash, bytes _sig)
internal
pure
returns (address)
{
bytes32 r;
bytes32 s;
uint8 v;
if (_sig.length != 65) {
return (address(0));
}
assembly {
r := mload(add(_sig, 32))
s := mload(add(_sig, 64))
v := byte(0, mload(add(_sig, 96)))
}
if (v < 27) {
v += 27;
}
if (v != 27 && v != 28) {
return (address(0));
} else {
return ecrecover(_hash, v, r, s);
}
}
function toEthSignedMessageHash(bytes32 _hash)
internal
pure
returns (bytes32)
{
return keccak256(
abi.encodePacked("\x19Ethereum Signed Message:\n32", _hash)
);
}
}
contract Utils2 {
using ECRecovery for bytes32;
function _recoverSigner(bytes32 _hash, bytes _signature)
internal
pure
returns(address _signer)
{
return _hash.toEthSignedMessageHash().recover(_signature);
}
}
contract Config is DSNote, DSAuth, Utils {
WETH9 public weth9;
mapping (address => bool) public isAccountHandler;
mapping (address => bool) public isAdmin;
address[] public admins;
bool public disableAdminControl = false;
event LogAdminAdded(address indexed _admin, address _by);
event LogAdminRemoved(address indexed _admin, address _by);
constructor() public {
admins.push(msg.sender);
isAdmin[msg.sender] = true;
}
modifier onlyAdmin(){
require(isAdmin[msg.sender], "Config::_ SENDER_NOT_AUTHORIZED");
_;
}
function setWETH9
(
address _weth9
)
public
auth
note
addressValid(_weth9)
{
weth9 = WETH9(_weth9);
}
function setAccountHandler
(
address _accountHandler,
bool _isAccountHandler
)
public
auth
note
addressValid(_accountHandler)
{
isAccountHandler[_accountHandler] = _isAccountHandler;
}
function toggleAdminsControl()
public
auth
note
{
disableAdminControl = !disableAdminControl;
}
function isAdminValid(address _admin)
public
view
returns (bool)
{
if(disableAdminControl) {
return true;
} else {
return isAdmin[_admin];
}
}
function getAllAdmins()
public
view
returns(address[])
{
return admins;
}
function addAdmin
(
address _admin
)
external
note
onlyAdmin
addressValid(_admin)
{
require(!isAdmin[_admin], "Config::addAdmin ADMIN_ALREADY_EXISTS");
admins.push(_admin);
isAdmin[_admin] = true;
emit LogAdminAdded(_admin, msg.sender);
}
function removeAdmin
(
address _admin
)
external
note
onlyAdmin
addressValid(_admin)
{
require(isAdmin[_admin], "Config::removeAdmin ADMIN_DOES_NOT_EXIST");
require(msg.sender != _admin, "Config::removeAdmin ADMIN_NOT_AUTHORIZED");
isAdmin[_admin] = false;
for (uint i = 0; i < admins.length - 1; i++) {
if (admins[i] == _admin) {
admins[i] = admins[admins.length - 1];
admins.length -= 1;
break;
}
}
emit LogAdminRemoved(_admin, msg.sender);
}
}
contract DSStop is DSNote, DSAuth {
bool public stopped = false;
modifier whenNotStopped {
require(!stopped, "DSStop::_ FEATURE_STOPPED");
_;
}
modifier whenStopped {
require(stopped, "DSStop::_ FEATURE_NOT_STOPPED");
_;
}
function stop() public auth note {
stopped = true;
}
function start() public auth note {
stopped = false;
}
}
contract Account is MasterCopy, DSNote, Utils, Utils2, ErrorUtils {
address[] public users;
mapping (address => bool) public isUser;
mapping (bytes32 => bool) public actionCompleted;
WETH9 public weth9;
Config public config;
bool public isInitialized = false;
event LogTransferBySystem(address indexed token, address indexed to, uint value, address by);
event LogTransferByUser(address indexed token, address indexed to, uint value, address by);
event LogUserAdded(address indexed user, address by);
event LogUserRemoved(address indexed user, address by);
modifier initialized() {
require(isInitialized, "Account::_ ACCOUNT_NOT_INITIALIZED");
_;
}
modifier userExists(address _user) {
require(isUser[_user], "Account::_ INVALID_USER");
_;
}
modifier userDoesNotExist(address _user) {
require(!isUser[_user], "Account::_ USER_DOES_NOT_EXISTS");
_;
}
modifier onlyHandler(){
require(config.isAccountHandler(msg.sender), "Account::_ INVALID_ACC_HANDLER");
_;
}
function init(address _user, address _config) public {
users.push(_user);
isUser[_user] = true;
config = Config(_config);
weth9 = config.weth9();
isInitialized = true;
}
function getAllUsers() public view returns (address[]) {
return users;
}
function balanceFor(address _token) public view returns (uint _balance){
_balance = ERC20(_token).balanceOf(this);
}
function transferBySystem
(
address _token,
address _to,
uint _value
)
external
onlyHandler
note
initialized
{
require(ERC20(_token).balanceOf(this) >= _value, "Account::transferBySystem INSUFFICIENT_BALANCE_IN_ACCOUNT");
require(ERC20(_token).transfer(_to, _value), "Account::transferBySystem TOKEN_TRANSFER_FAILED");
emit LogTransferBySystem(_token, _to, _value, msg.sender);
}
function transferByUser
(
address _token,
address _to,
uint _value,
uint _salt,
bytes _signature
)
external
addressValid(_to)
note
initialized
{
bytes32 actionHash = _getTransferActionHash(_token, _to, _value, _salt);
if(actionCompleted[actionHash]) {
emit LogError("Account::transferByUser", "ACTION_ALREADY_PERFORMED");
return;
}
if(ERC20(_token).balanceOf(this) < _value){
emit LogError("Account::transferByUser", "INSUFFICIENT_BALANCE_IN_ACCOUNT");
return;
}
address signer = _recoverSigner(actionHash, _signature);
if(!isUser[signer]) {
emit LogError("Account::transferByUser", "SIGNER_NOT_AUTHORIZED_WITH_ACCOUNT");
return;
}
actionCompleted[actionHash] = true;
if (_token == address(weth9)) {
weth9.withdraw(_value);
_to.transfer(_value);
} else {
require(ERC20(_token).transfer(_to, _value), "Account::transferByUser TOKEN_TRANSFER_FAILED");
}
emit LogTransferByUser(_token, _to, _value, signer);
}
function addUser
(
address _user,
uint _salt,
bytes _signature
)
external
note
addressValid(_user)
userDoesNotExist(_user)
initialized
{
bytes32 actionHash = _getUserActionHash(_user, "ADD_USER", _salt);
if(actionCompleted[actionHash])
{
emit LogError("Account::addUser", "ACTION_ALREADY_PERFORMED");
return;
}
address signer = _recoverSigner(actionHash, _signature);
if(!isUser[signer]) {
emit LogError("Account::addUser", "SIGNER_NOT_AUTHORIZED_WITH_ACCOUNT");
return;
}
actionCompleted[actionHash] = true;
users.push(_user);
isUser[_user] = true;
emit LogUserAdded(_user, signer);
}
function removeUser
(
address _user,
uint _salt,
bytes _signature
)
external
note
userExists(_user)
initialized
{
bytes32 actionHash = _getUserActionHash(_user, "REMOVE_USER", _salt);
if(actionCompleted[actionHash]) {
emit LogError("Account::removeUser", "ACTION_ALREADY_PERFORMED");
return;
}
address signer = _recoverSigner(actionHash, _signature);
// require(signer != _user, "Account::removeUser SIGNER_NOT_AUTHORIZED_WITH_ACCOUNT");
if(!isUser[signer]){
emit LogError("Account::removeUser", "SIGNER_NOT_AUTHORIZED_WITH_ACCOUNT");
return;
}
actionCompleted[actionHash] = true;
isUser[_user] = false;
for (uint i = 0; i < users.length - 1; i++) {
if (users[i] == _user) {
users[i] = users[users.length - 1];
users.length -= 1;
break;
}
}
emit LogUserRemoved(_user, signer);
}
function _getTransferActionHash
(
address _token,
address _to,
uint _value,
uint _salt
)
internal
view
returns (bytes32)
{
return keccak256(
abi.encodePacked(
address(this),
_token,
_to,
_value,
_salt
)
);
}
function _getUserActionHash
(
address _user,
string _action,
uint _salt
)
internal
view
returns (bytes32)
{
return keccak256(
abi.encodePacked(
address(this),
_user,
_action,
_salt
)
);
}
// to directly send ether to contract
function() external payable {
require(msg.data.length == 0 && msg.value > 0, "Account::fallback INVALID_ETHER_TRANSFER");
if(msg.sender != address(weth9)){
weth9.deposit.value(msg.value)();
}
}
}
contract AccountFactory is DSStop, Utils {
Config public config;
mapping (address => bool) public isAccount;
mapping (address => address[]) public userToAccounts;
address[] public accounts;
address public accountMaster;
constructor
(
Config _config,
address _accountMaster
)
public
{
config = _config;
accountMaster = _accountMaster;
}
event LogAccountCreated(address indexed user, address indexed account, address by);
modifier onlyAdmin() {
require(config.isAdminValid(msg.sender), "AccountFactory::_ INVALID_ADMIN_ACCOUNT");
_;
}
function setConfig(Config _config) external note auth addressValid(_config) {
config = _config;
}
function setAccountMaster(address _accountMaster) external note auth addressValid(_accountMaster) {
accountMaster = _accountMaster;
}
function newAccount(address _user)
public
note
onlyAdmin
addressValid(config)
addressValid(accountMaster)
whenNotStopped
returns
(
Account _account
)
{
address proxy = new Proxy(accountMaster);
_account = Account(proxy);
_account.init(_user, config);
accounts.push(_account);
userToAccounts[_user].push(_account);
isAccount[_account] = true;
emit LogAccountCreated(_user, _account, msg.sender);
}
function batchNewAccount(address[] _users) public note onlyAdmin {
for (uint i = 0; i < _users.length; i++) {
newAccount(_users[i]);
}
}
function getAllAccounts() public view returns (address[]) {
return accounts;
}
function getAccountsForUser(address _user) public view returns (address[]) {
return userToAccounts[_user];
}
}