Cryo Explorer Ethereum Mainnet

Address Contract Partially Verified

Address 0xA6cd930Fc92F1634d8183af2Fb86bd1766f2f82a
Balance 384.1803 ETH ($742797.24)
Nonce 1
Code Size 5999 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

5999 bytes
0x60806040526004361061013c576000357c01000000000000000000000000000000000000000000000000000000009004806380ba952e116100bd578063a96a5f9411610081578063a96a5f94146104f9578063bfa2c1d214610523578063c108bb4014610566578063cafd4600146105a5578063d68d9d4e146105de5761013c565b806380ba952e146103e057806382dc1ec41461042f5780638456cb59146104625780638e0cc17614610477578063a0c89a8c146104c05761013c565b80633f4ba83a116101045780633f4ba83a1461032157806346fbf68e14610336578063530e931c1461037d5780635c975abb146103b65780636ef8d66d146103cb5761013c565b80630d63a1fd1461014157806314da2906146102115780631687cc6014610257578063323c4480146102d157806336cc9e8d1461030c575b600080fd5b34801561014d57600080fd5b506101ff6004803603606081101561016457600080fd5b81019060208101813564010000000081111561017f57600080fd5b82018360208201111561019157600080fd5b803590602001918460208302840111640100000000831117156101b357600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092955050600160a060020a0383351693505050602001356105fb565b60408051918252519081900360200190f35b34801561021d57600080fd5b5061023b6004803603602081101561023457600080fd5b50356107e5565b60408051600160a060020a039092168252519081900360200190f35b34801561026357600080fd5b506102816004803603602081101561027a57600080fd5b5035610807565b60408051602080825283518183015283519192839290830191858101910280838360005b838110156102bd5781810151838201526020016102a5565b505050509050019250505060405180910390f35b3480156102dd57600080fd5b5061030a600480360360408110156102f457600080fd5b5080359060200135600160a060020a0316610873565b005b34801561031857600080fd5b506101ff6109ed565b34801561032d57600080fd5b5061030a6109f3565b34801561034257600080fd5b506103696004803603602081101561035957600080fd5b5035600160a060020a0316610a53565b604080519115158252519081900360200190f35b34801561038957600080fd5b506101ff600480360360408110156103a057600080fd5b5080359060200135600160a060020a0316610a6b565b3480156103c257600080fd5b50610369610a97565b3480156103d757600080fd5b5061030a610aa1565b3480156103ec57600080fd5b5061030a600480360360a081101561040357600080fd5b50803590602081013590600160a060020a03604082013581169160608101359091169060800135610aac565b34801561043b57600080fd5b5061030a6004803603602081101561045257600080fd5b5035600160a060020a0316610c46565b34801561046e57600080fd5b5061030a610c64565b34801561048357600080fd5b5061030a6004803603608081101561049a57600080fd5b50803590600160a060020a03602082013581169160408101359091169060600135610cc7565b3480156104cc57600080fd5b5061030a600480360360408110156104e357600080fd5b5080359060200135600160a060020a0316610e01565b34801561050557600080fd5b5061023b6004803603602081101561051c57600080fd5b5035610e93565b34801561052f57600080fd5b5061030a6004803603606081101561054657600080fd5b50600160a060020a03813581169160208101359091169060400135610eb1565b34801561057257600080fd5b5061030a6004803603606081101561058957600080fd5b50803590600160a060020a036020820135169060400135610f28565b3480156105b157600080fd5b50610369600480360360408110156105c857600080fd5b5080359060200135600160a060020a0316610fa0565b61030a600480360360208110156105f457600080fd5b503561101e565b60015460009060ff161561060e57600080fd5b600160a060020a03831661066c576040805160e560020a62461bcd02815260206004820152601a60248201527f4e6577206f70657261746f722069732061646472657373283029000000000000604482015290519081900360640190fd5b604080516c010000000000000000000000003081026020808401919091523391909102603483015260488083018690528351808403909101815260689092018352815191810191909120600081815260039092529190206001810154600160a060020a031615610726576040805160e560020a62461bcd02815260206004820152601260248201527f4f636375706965642077616c6c65742069640000000000000000000000000000604482015290519081900360640190fd5b85516107389082906020890190611676565b506001818101805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a03881690811790915560028054909201909155604051875188919081906020808501910280838360005b838110156107a0578181015183820152602001610788565b505060405192909401829003822095508894507fe778e91533ef049a5fc99752bc4efb2b50ca4c967dfc0d4bb4782fb128070c3493506000925050a450949350505050565b60008181526003602081905260409091200154600160a060020a03165b919050565b60008181526003602090815260409182902080548351818402810184019094528084526060939283018282801561086757602002820191906000526020600020905b8154600160a060020a03168152600190910190602001808311610849575b50505050509050919050565b813361087f8282611078565b6108bd5760405160e560020a62461bcd02815260040180806020018281038252602181526020018061171a6021913960400191505060405180910390fd5b600160a060020a03831661091b576040805160e560020a62461bcd02815260206004820152601a60248201527f4e6577206f70657261746f722069732061646472657373283029000000000000604482015290519081900360640190fd5b600084815260036020819052604090912090810154600160a060020a038581169116146109765761094b816110dd565b60038101805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0386161790555b336000818152600483016020526040808220805460ff1916600117905551600160a060020a0387169188917f71f9e7796b33cb192d1670169ee7f4af7c5364f8f01bab4b95466787593745c39190a46109ce81611140565b156109e6576109dd85856111a9565b6109e6816110dd565b5050505050565b60025481565b6109fc33610a53565b610a0557600080fd5b60015460ff16610a1457600080fd5b6001805460ff191690556040805133815290517f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa9181900360200190a1565b6000610a65818363ffffffff61127b16565b92915050565b6000828152600360209081526040808320600160a060020a038516845260020190915290205492915050565b60015460ff165b90565b610aaa336112b0565b565b60015460ff1615610abc57600080fd5b6000858152600360205260409020600101548590600160a060020a03163314610b2f576040805160e560020a62461bcd02815260206004820152601a60248201527f6d73672e73656e646572206973206e6f74206f70657261746f72000000000000604482015290519081900360640190fd5b8583610b3b8282611078565b610b795760405160e560020a62461bcd02815260040180806020018281038252602181526020018061171a6021913960400191505060405180910390fd5b8685610b858282611078565b610bc35760405160e560020a62461bcd02815260040180806020018281038252602181526020018061171a6021913960400191505060405180910390fd5b610bd08a898860016112f8565b610bdd89898860006112f8565b87600160a060020a0316898b7f1b56f805e5edb1e61b0d3f46feffdcbab5e591aa0e70e978ada9fc22093601c88a8a6040518083600160a060020a0316600160a060020a031681526020018281526020019250505060405180910390a450505050505050505050565b610c4f33610a53565b610c5857600080fd5b610c61816113a5565b50565b610c6d33610a53565b610c7657600080fd5b60015460ff1615610c8657600080fd5b6001805460ff1916811790556040805133815290517f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2589181900360200190a1565b60015460ff1615610cd757600080fd5b6000848152600360205260409020600101548490600160a060020a03163314610d4a576040805160e560020a62461bcd02815260206004820152601a60248201527f6d73672e73656e646572206973206e6f74206f70657261746f72000000000000604482015290519081900360640190fd5b8483610d568282611078565b610d945760405160e560020a62461bcd02815260040180806020018281038252602181526020018061171a6021913960400191505060405180910390fd5b610da187878660016112f8565b84600160a060020a031686600160a060020a0316887fd897e862036b62a0f770979fbd2227f3210565bba2eb4d9acd1dc8ccc00c928b876040518082815260200191505060405180910390a4610df88686866113ed565b50505050505050565b60015460ff1615610e1157600080fd5b6000828152600360205260409020600101548290600160a060020a03163314610e84576040805160e560020a62461bcd02815260206004820152601a60248201527f6d73672e73656e646572206973206e6f74206f70657261746f72000000000000604482015290519081900360640190fd5b610e8e83836111a9565b505050565b600090815260036020526040902060010154600160a060020a031690565b60015460ff16610ec057600080fd5b610ec933610a53565b610ed257600080fd5b81600160a060020a031683600160a060020a03167f896ecb17b26927fb33933fc5f413873193bced3c59fe736c42968a9778bf6b58836040518082815260200191505060405180910390a3610e8e8383836113ed565b60015460ff1615610f3857600080fd5b610f4583838360006112f8565b604080518281529051600160a060020a0384169185917fbc8e388b96ba8b9f627cb6d72d3513182f763c33c6107ecd31191de1f71abc1a9181900360200190a3610e8e600160a060020a03831633308463ffffffff61145416565b60008282610fae8282611078565b610fec5760405160e560020a62461bcd02815260040180806020018281038252602181526020018061171a6021913960400191505060405180910390fd5b5050506000918252600360209081526040808420600160a060020a039390931684526004909201905290205460ff1690565b60015460ff161561102e57600080fd5b3461103c82600083816112f8565b60408051828152905160009184917fbc8e388b96ba8b9f627cb6d72d3513182f763c33c6107ecd31191de1f71abc1a9181900360200190a35050565b6000828152600360205260408120815b81548110156110d2578160000181815481106110a057fe5b600091825260209091200154600160a060020a03858116911614156110ca57600192505050610a65565b600101611088565b506000949350505050565b60005b815481101561113c57600082600401600084600001848154811061110057fe5b600091825260208083209190910154600160a060020a031683528201929092526040019020805460ff19169115159190911790556001016110e0565b5050565b6000805b82548110156111a05782600401600084600001838154811061116257fe5b6000918252602080832090910154600160a060020a0316835282019290925260400190205460ff16611198576000915050610802565b600101611144565b50600192915050565b600160a060020a038116611207576040805160e560020a62461bcd02815260206004820152601a60248201527f4e6577206f70657261746f722069732061646472657373283029000000000000604482015290519081900360640190fd5b600082815260036020526040808220600181018054600160a060020a0386811673ffffffffffffffffffffffffffffffffffffffff1983168117909355935192949316929091839187917f118c3f8030bc3c8254e737a0bd0584403c33646afbcbee8321c3bd5b26543cda9190a450505050565b6000600160a060020a03821661129057600080fd5b50600160a060020a03166000908152602091909152604090205460ff1690565b6112c160008263ffffffff61150316565b604051600160a060020a038216907fcd265ebaf09df2871cc7bd4133404a235ba12eff2041bb89d9c714a2621c7c7e90600090a250565b60008481526003602052604081209082600181111561131357fe5b141561136457600160a060020a0384166000908152600282016020526040902054611344908463ffffffff61154b16565b600160a060020a03851660009081526002830160205260409020556109e6565b600182600181111561137257fe5b14156113a357600160a060020a0384166000908152600282016020526040902054611344908463ffffffff61156416565bfe5b6113b660008263ffffffff61157916565b604051600160a060020a038216907f6719d08c1888103bea251a4ed56406bd0c3e69723c8a1686e017e7bbe159b6f890600090a250565b600160a060020a03831661143a576040518290600160a060020a0382169083156108fc029084906000818181858888f19350505050158015611433573d6000803e3d6000fd5b5050610e8e565b610e8e600160a060020a038416838363ffffffff6115c516565b604080517f23b872dd000000000000000000000000000000000000000000000000000000008152600160a060020a0385811660048301528481166024830152604482018490529151918616916323b872dd916064808201926020929091908290030181600087803b1580156114c857600080fd5b505af11580156114dc573d6000803e3d6000fd5b505050506040513d60208110156114f257600080fd5b50516114fd57600080fd5b50505050565b600160a060020a03811661151657600080fd5b611520828261127b565b61152957600080fd5b600160a060020a0316600090815260209190915260409020805460ff19169055565b60008282018381101561155d57600080fd5b9392505050565b60008282111561157357600080fd5b50900390565b600160a060020a03811661158c57600080fd5b611596828261127b565b156115a057600080fd5b600160a060020a0316600090815260209190915260409020805460ff19166001179055565b82600160a060020a031663a9059cbb83836040518363ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004018083600160a060020a0316600160a060020a0316815260200182815260200192505050602060405180830381600087803b15801561164157600080fd5b505af1158015611655573d6000803e3d6000fd5b505050506040513d602081101561166b57600080fd5b5051610e8e57600080fd5b8280548282559060005260206000209081019282156116d8579160200282015b828111156116d8578251825473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a03909116178255602090920191600190910190611696565b506116e49291506116e8565b5090565b610a9e91905b808211156116e457805473ffffffffffffffffffffffffffffffffffffffff191681556001016116ee56fe476976656e2061646472657373206973206e6f742077616c6c6574206f776e6572a265627a7a72305820a267d9485b9aea3cd16e5d5b2dc6fc9eaf93731dd38628af3c5eebbf47de01e064736f6c634300050a0032

Verified Source Code Partial Match

Compiler: v0.5.10+commit.5a6ea5b1 EVM: byzantium Optimization: Yes (200 runs)
CelerWallet.sol 723 lines
// File: contracts/lib/interface/ICelerWallet.sol

pragma solidity ^0.5.1;

/**
 * @title CelerWallet interface
 */
interface ICelerWallet {
    function create(address[] calldata _owners, address _operator, bytes32 _nonce) external returns(bytes32);

    function depositETH(bytes32 _walletId) external payable;

    function depositERC20(bytes32 _walletId, address _tokenAddress, uint _amount) external;
    
    function withdraw(bytes32 _walletId, address _tokenAddress, address _receiver, uint _amount) external;

    function transferToWallet(bytes32 _fromWalletId, bytes32 _toWalletId, address _tokenAddress, address _receiver, uint _amount) external;

    function transferOperatorship(bytes32 _walletId, address _newOperator) external;

    function proposeNewOperator(bytes32 _walletId, address _newOperator) external;

    function drainToken(address _tokenAddress, address _receiver, uint _amount) external;

    function getWalletOwners(bytes32 _walletId) external view returns(address[] memory);

    function getOperator(bytes32 _walletId) external view returns(address);

    function getBalance(bytes32 _walletId, address _tokenAddress) external view returns(uint);

    function getProposedNewOperator(bytes32 _walletId) external view returns(address);

    function getProposalVote(bytes32 _walletId, address _owner) external view returns(bool);

    event CreateWallet(bytes32 indexed walletId, address[] indexed owners, address indexed operator);

    event DepositToWallet(bytes32 indexed walletId, address indexed tokenAddress, uint amount);

    event WithdrawFromWallet(bytes32 indexed walletId, address indexed tokenAddress, address indexed receiver, uint amount);

    event TransferToWallet(bytes32 indexed fromWalletId, bytes32 indexed toWalletId, address indexed tokenAddress, address receiver, uint amount);

    event ChangeOperator(bytes32 indexed walletId, address indexed oldOperator, address indexed newOperator);

    event ProposeNewOperator(bytes32 indexed walletId, address indexed newOperator, address indexed proposer);

    event DrainToken(address indexed tokenAddress, address indexed receiver, uint amount);
}

// File: openzeppelin-solidity/contracts/math/SafeMath.sol

pragma solidity ^0.5.0;

/**
 * @title SafeMath
 * @dev Unsigned math operations with safety checks that revert on error
 */
library SafeMath {
    /**
    * @dev Multiplies two unsigned integers, reverts on overflow.
    */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
        if (a == 0) {
            return 0;
        }

        uint256 c = a * b;
        require(c / a == b);

        return c;
    }

    /**
    * @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero.
    */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        // Solidity only automatically asserts when dividing by 0
        require(b > 0);
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold

        return c;
    }

    /**
    * @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend).
    */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b <= a);
        uint256 c = a - b;

        return c;
    }

    /**
    * @dev Adds two unsigned integers, reverts on overflow.
    */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a);

        return c;
    }

    /**
    * @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo),
    * reverts when dividing by zero.
    */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b != 0);
        return a % b;
    }
}

// File: openzeppelin-solidity/contracts/token/ERC20/IERC20.sol

pragma solidity ^0.5.0;

/**
 * @title ERC20 interface
 * @dev see https://github.com/ethereum/EIPs/issues/20
 */
interface IERC20 {
    function transfer(address to, uint256 value) external returns (bool);

    function approve(address spender, uint256 value) external returns (bool);

    function transferFrom(address from, address to, uint256 value) external returns (bool);

    function totalSupply() external view returns (uint256);

    function balanceOf(address who) external view returns (uint256);

    function allowance(address owner, address spender) external view returns (uint256);

    event Transfer(address indexed from, address indexed to, uint256 value);

    event Approval(address indexed owner, address indexed spender, uint256 value);
}

// File: openzeppelin-solidity/contracts/token/ERC20/SafeERC20.sol

pragma solidity ^0.5.0;



/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure.
 * To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using SafeMath for uint256;

    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        require(token.transfer(to, value));
    }

    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        require(token.transferFrom(from, to, value));
    }

    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require((value == 0) || (token.allowance(msg.sender, spender) == 0));
        require(token.approve(spender, value));
    }

    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).add(value);
        require(token.approve(spender, newAllowance));
    }

    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).sub(value);
        require(token.approve(spender, newAllowance));
    }
}

// File: openzeppelin-solidity/contracts/access/Roles.sol

pragma solidity ^0.5.0;

/**
 * @title Roles
 * @dev Library for managing addresses assigned to a Role.
 */
library Roles {
    struct Role {
        mapping (address => bool) bearer;
    }

    /**
     * @dev give an account access to this role
     */
    function add(Role storage role, address account) internal {
        require(account != address(0));
        require(!has(role, account));

        role.bearer[account] = true;
    }

    /**
     * @dev remove an account's access to this role
     */
    function remove(Role storage role, address account) internal {
        require(account != address(0));
        require(has(role, account));

        role.bearer[account] = false;
    }

    /**
     * @dev check if an account has this role
     * @return bool
     */
    function has(Role storage role, address account) internal view returns (bool) {
        require(account != address(0));
        return role.bearer[account];
    }
}

// File: openzeppelin-solidity/contracts/access/roles/PauserRole.sol

pragma solidity ^0.5.0;


contract PauserRole {
    using Roles for Roles.Role;

    event PauserAdded(address indexed account);
    event PauserRemoved(address indexed account);

    Roles.Role private _pausers;

    constructor () internal {
        _addPauser(msg.sender);
    }

    modifier onlyPauser() {
        require(isPauser(msg.sender));
        _;
    }

    function isPauser(address account) public view returns (bool) {
        return _pausers.has(account);
    }

    function addPauser(address account) public onlyPauser {
        _addPauser(account);
    }

    function renouncePauser() public {
        _removePauser(msg.sender);
    }

    function _addPauser(address account) internal {
        _pausers.add(account);
        emit PauserAdded(account);
    }

    function _removePauser(address account) internal {
        _pausers.remove(account);
        emit PauserRemoved(account);
    }
}

// File: openzeppelin-solidity/contracts/lifecycle/Pausable.sol

pragma solidity ^0.5.0;


/**
 * @title Pausable
 * @dev Base contract which allows children to implement an emergency stop mechanism.
 */
contract Pausable is PauserRole {
    event Paused(address account);
    event Unpaused(address account);

    bool private _paused;

    constructor () internal {
        _paused = false;
    }

    /**
     * @return true if the contract is paused, false otherwise.
     */
    function paused() public view returns (bool) {
        return _paused;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     */
    modifier whenNotPaused() {
        require(!_paused);
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     */
    modifier whenPaused() {
        require(_paused);
        _;
    }

    /**
     * @dev called by the owner to pause, triggers stopped state
     */
    function pause() public onlyPauser whenNotPaused {
        _paused = true;
        emit Paused(msg.sender);
    }

    /**
     * @dev called by the owner to unpause, returns to normal state
     */
    function unpause() public onlyPauser whenPaused {
        _paused = false;
        emit Unpaused(msg.sender);
    }
}

// File: contracts/CelerWallet.sol

pragma solidity ^0.5.1;






/**
 * @title CelerWallet contract
 * @notice A multi-owner, multi-token, operator-centric wallet designed for CelerChannel.
 *   This wallet can run independetly and doesn't rely on trust of any external contracts
 *   even CelerLedger to maximize its security.
 */
contract CelerWallet is ICelerWallet, Pausable {
    using SafeMath for uint;
    using SafeERC20 for IERC20;

    enum MathOperation { Add, Sub }

    struct Wallet {
        // corresponding to peers in CelerLedger
        address[] owners;
        // corresponding to CelerLedger
        address operator;
        // adderss(0) for ETH
        mapping(address => uint) balances;
        address proposedNewOperator;
        mapping(address => bool) proposalVotes;
    }

    uint public walletNum;
    mapping(bytes32 => Wallet) private wallets;

    /**
     * @dev Throws if called by any account other than the wallet's operator
     * @param _walletId id of the wallet to be operated
     */
    modifier onlyOperator(bytes32 _walletId) {
        require(msg.sender == wallets[_walletId].operator, "msg.sender is not operator");
        _;
    }

    /**
     * @dev Throws if given address is not an owner of the wallet
     * @param _walletId id of the wallet to be operated
     * @param _addr address to be checked
     */
    modifier onlyWalletOwner(bytes32 _walletId, address _addr) {
        require(_isWalletOwner(_walletId, _addr), "Given address is not wallet owner");
        _;
    }

    /**
     * @notice Create a new wallet
     * @param _owners owners of the wallet
     * @param _operator initial operator of the wallet
     * @param _nonce nonce given by caller to generate the wallet id
     * @return id of created wallet
     */
    function create(
        address[] memory _owners,
        address _operator,
        bytes32 _nonce
    )
        public
        whenNotPaused
        returns(bytes32)
    {
        require(_operator != address(0), "New operator is address(0)");

        bytes32 walletId = keccak256(abi.encodePacked(address(this), msg.sender, _nonce));
        Wallet storage w = wallets[walletId];
        // wallet must be uninitialized
        require(w.operator == address(0), "Occupied wallet id");
        w.owners = _owners;
        w.operator = _operator;
        walletNum++;

        emit CreateWallet(walletId, _owners, _operator);
        return walletId;
    }

    /**
     * @notice Deposit ETH to a wallet
     * @param _walletId id of the wallet to deposit into
     */
    function depositETH(bytes32 _walletId) public payable whenNotPaused {
        uint amount = msg.value;
        _updateBalance(_walletId, address(0), amount, MathOperation.Add);
        emit DepositToWallet(_walletId, address(0), amount);
    }

    /**
     * @notice Deposit ERC20 tokens to a wallet
     * @param _walletId id of the wallet to deposit into
     * @param _tokenAddress address of token to deposit
     * @param _amount deposit token amount
     */
    function depositERC20(
        bytes32 _walletId,
        address _tokenAddress,
        uint _amount
    )
        public
        whenNotPaused
    {
        _updateBalance(_walletId, _tokenAddress, _amount, MathOperation.Add);
        emit DepositToWallet(_walletId, _tokenAddress, _amount);

        IERC20(_tokenAddress).safeTransferFrom(msg.sender, address(this), _amount);
    }

    /**
     * @notice Withdraw funds to an address
     * @dev Since this withdraw() function uses direct transfer to send ETH, if CelerLedger
     *   allows non externally-owned account (EOA) to be a peer of the channel namely an owner
     *   of the wallet, CelerLedger should implement a withdraw pattern for ETH to avoid
     *   maliciously fund locking. Withdraw pattern reference:
     *   https://solidity.readthedocs.io/en/v0.5.9/common-patterns.html#withdrawal-from-contracts
     * @param _walletId id of the wallet to withdraw from
     * @param _tokenAddress address of token to withdraw
     * @param _receiver token receiver
     * @param _amount withdrawal token amount
     */
    function withdraw(
        bytes32 _walletId,
        address _tokenAddress,
        address _receiver,
        uint _amount
    )
        public
        whenNotPaused
        onlyOperator(_walletId)
        onlyWalletOwner(_walletId, _receiver)
    {
        _updateBalance(_walletId, _tokenAddress, _amount, MathOperation.Sub);
        emit WithdrawFromWallet(_walletId, _tokenAddress, _receiver, _amount);

        _withdrawToken(_tokenAddress, _receiver, _amount);
    }

    /**
     * @notice Transfer funds from one wallet to another wallet with a same owner (as the receiver)
     * @dev from wallet and to wallet must have one common owner as the receiver or beneficiary
     *   of this transfer
     * @param _fromWalletId id of wallet to transfer funds from
     * @param _toWalletId id of wallet to transfer funds to
     * @param _tokenAddress address of token to transfer
     * @param _receiver beneficiary who transfers her funds from one wallet to another wallet
     * @param _amount transferred token amount
     */
    function transferToWallet(
        bytes32 _fromWalletId,
        bytes32 _toWalletId,
        address _tokenAddress,
        address _receiver,
        uint _amount
    )
        public
        whenNotPaused
        onlyOperator(_fromWalletId)
        onlyWalletOwner(_fromWalletId, _receiver)
        onlyWalletOwner(_toWalletId, _receiver)
    {
        _updateBalance(_fromWalletId, _tokenAddress, _amount, MathOperation.Sub);
        _updateBalance(_toWalletId, _tokenAddress, _amount, MathOperation.Add);
        emit TransferToWallet(_fromWalletId, _toWalletId, _tokenAddress, _receiver, _amount);
    }

    /**
     * @notice Current operator transfers the operatorship of a wallet to the new operator
     * @param _walletId id of wallet to transfer the operatorship
     * @param _newOperator the new operator
     */
    function transferOperatorship(
        bytes32 _walletId,
        address _newOperator
    )
        public
        whenNotPaused
        onlyOperator(_walletId)
    {
        _changeOperator(_walletId, _newOperator);
    }

    /**
     * @notice Wallet owners propose and assign a new operator of their wallet
     * @dev it will assign a new operator if all owners propose the same new operator.
     *   This does not require unpaused.
     * @param _walletId id of wallet which owners propose new operator of
     * @param _newOperator the new operator proposal
     */
    function proposeNewOperator(
        bytes32 _walletId,
        address _newOperator
    )
        public
        onlyWalletOwner(_walletId, msg.sender)
    {
        require(_newOperator != address(0), "New operator is address(0)");

        Wallet storage w = wallets[_walletId];
        if (_newOperator != w.proposedNewOperator) {
            _clearVotes(w);
            w.proposedNewOperator = _newOperator;
        }

        w.proposalVotes[msg.sender] = true;
        emit ProposeNewOperator(_walletId, _newOperator, msg.sender);

        if (_checkAllVotes(w)) {
            _changeOperator(_walletId, _newOperator);
            _clearVotes(w);
        }
    }

    /**
     * @notice Pauser drains one type of tokens when paused
     * @dev This is for emergency situations.
     * @param _tokenAddress address of token to drain
     * @param _receiver token receiver
     * @param _amount drained token amount
     */
    function drainToken(
        address _tokenAddress,
        address _receiver,
        uint _amount
    )
        public
        whenPaused
        onlyPauser
    {
        emit DrainToken(_tokenAddress, _receiver, _amount);

        _withdrawToken(_tokenAddress, _receiver, _amount);
    }

    /**
     * @notice Get owners of a given wallet
     * @param _walletId id of the queried wallet
     * @return wallet's owners
     */
    function getWalletOwners(bytes32 _walletId) external view returns(address[] memory) {
        return wallets[_walletId].owners;
    }

    /**
     * @notice Get operator of a given wallet
     * @param _walletId id of the queried wallet
     * @return wallet's operator
     */
    function getOperator(bytes32 _walletId) public view returns(address) {
        return wallets[_walletId].operator;
    }

    /**
     * @notice Get balance of a given token in a given wallet
     * @param _walletId id of the queried wallet
     * @param _tokenAddress address of the queried token
     * @return amount of the given token in the wallet
     */
    function getBalance(bytes32 _walletId, address _tokenAddress) public view returns(uint) {
        return wallets[_walletId].balances[_tokenAddress];
    }

    /**
     * @notice Get proposedNewOperator of a given wallet
     * @param _walletId id of the queried wallet
     * @return wallet's proposedNewOperator
     */
    function getProposedNewOperator(bytes32 _walletId) external view returns(address) {
        return wallets[_walletId].proposedNewOperator;

    }

    /**
     * @notice Get the vote of an owner for the proposedNewOperator of a wallet
     * @param _walletId id of the queried wallet
     * @param _owner owner to be checked
     * @return the owner's vote for the proposedNewOperator
     */
    function getProposalVote(
        bytes32 _walletId,
        address _owner
    )
        external
        view
        onlyWalletOwner(_walletId, _owner)
        returns(bool)
    {
        return wallets[_walletId].proposalVotes[_owner];
    }

    /**
     * @notice Internal function to withdraw out one type of token
     * @param _tokenAddress address of token to withdraw
     * @param _receiver token receiver
     * @param _amount withdrawal token amount
     */
    function _withdrawToken(address _tokenAddress, address _receiver, uint _amount) internal {
        if (_tokenAddress == address(0)) {
            // convert from address to address payable
            // TODO: latest version of openzeppelin Address.sol provide this api toPayable()
            address payable receiver  = address(uint160(_receiver));
            receiver.transfer(_amount);
        } else {
            IERC20(_tokenAddress).safeTransfer(_receiver, _amount);
        }
    }

    /**
     * @notice Update balance record
     * @param _walletId id of wallet to update
     * @param _tokenAddress address of token to update
     * @param _amount update amount
     * @param _op update operation
     */
    function _updateBalance(
        bytes32 _walletId,
        address _tokenAddress,
        uint _amount,
        MathOperation _op
    )
        internal
    {
        Wallet storage w = wallets[_walletId];
        if (_op == MathOperation.Add) {
            w.balances[_tokenAddress] = w.balances[_tokenAddress].add(_amount);
        } else if (_op == MathOperation.Sub) {
            w.balances[_tokenAddress] = w.balances[_tokenAddress].sub(_amount);
        } else {
            assert(false);
        }
    }

    /**
     * @notice Clear all votes of new operator proposals of the wallet
     * @param _w the wallet
     */
    function _clearVotes(Wallet storage _w) internal {
        for (uint i = 0; i < _w.owners.length; i++) {
            _w.proposalVotes[_w.owners[i]] = false;
        }
    }

    /**
     * @notice Internal function of changing the operator of a wallet
     * @param _walletId id of wallet to change its operator
     * @param _newOperator the new operator
     */
    function _changeOperator(bytes32 _walletId, address _newOperator) internal {
        require(_newOperator != address(0), "New operator is address(0)");

        Wallet storage w = wallets[_walletId];
        address oldOperator = w.operator;
        w.operator = _newOperator;
        emit ChangeOperator(_walletId, oldOperator, _newOperator);
    }

    /**
     * @notice Check if all owners have voted for the same new operator
     * @param _w the wallet
     * @return true if all owners have voted for a same operator; otherwise false
     */
    function _checkAllVotes(Wallet storage _w) internal view returns(bool) {
        for (uint i = 0; i < _w.owners.length; i++) {
            if (_w.proposalVotes[_w.owners[i]] == false) {
                return false;
            }
        }
        return true;
    }

    /**
     * @notice Check if an address is an owner of a wallet
     * @param _walletId id of wallet to check
     * @param _addr address to check
     * @return true if this address is an owner of the wallet; otherwise false
     */
    function _isWalletOwner(bytes32 _walletId, address _addr) internal view returns(bool) {
        Wallet storage w = wallets[_walletId];
        for (uint i = 0; i < w.owners.length; i++) {
            if (_addr == w.owners[i]) {
                return true;
            }
        }
        return false;
    }
}

Read Contract

getBalance 0x530e931c → uint256
getOperator 0xa96a5f94 → address
getProposalVote 0xcafd4600 → bool
getProposedNewOperator 0x14da2906 → address
getWalletOwners 0x1687cc60 → address[]
isPauser 0x46fbf68e → bool
paused 0x5c975abb → bool
walletNum 0x36cc9e8d → uint256

Write Contract 12 functions

These functions modify contract state and require a wallet transaction to execute.

addPauser 0x82dc1ec4
address account
create 0x0d63a1fd
address[] _owners
address _operator
bytes32 _nonce
returns: bytes32
depositERC20 0xc108bb40
bytes32 _walletId
address _tokenAddress
uint256 _amount
depositETH 0xd68d9d4e
bytes32 _walletId
drainToken 0xbfa2c1d2
address _tokenAddress
address _receiver
uint256 _amount
pause 0x8456cb59
No parameters
proposeNewOperator 0x323c4480
bytes32 _walletId
address _newOperator
renouncePauser 0x6ef8d66d
No parameters
transferOperatorship 0xa0c89a8c
bytes32 _walletId
address _newOperator
transferToWallet 0x80ba952e
bytes32 _fromWalletId
bytes32 _toWalletId
address _tokenAddress
address _receiver
uint256 _amount
unpause 0x3f4ba83a
No parameters
withdraw 0x8e0cc176
bytes32 _walletId
address _tokenAddress
address _receiver
uint256 _amount

Recent Transactions

No transactions found for this address