Contract Address Details

0x9b22f4A79000c5a23eE22506F85791232Fe47215

Contract Name
MultisigWallet
Creator
0xc7d98cā€“7f3521 at 0x8ddf07ā€“3239c1
Balance
0 CLO ( )
Tokens
Fetching tokens...
Transactions
14 Transactions
Transfers
15 Transfers
Gas Used
602,171
Last Balance Update
14745391
Contract name:
MultisigWallet




Optimization enabled
true
Compiler version
v0.8.7+commit.e28d00a7




Optimization runs
200
EVM Version
default




Verified at
2022-06-28T18:37:40.203046Z

Constructor Arguments

00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000cb9539df5a8617c4b39105f3d3a614ab72b94f5b0000000000000000000000004cc3a18209517a09f472639551357db80a17412b000000000000000000000000c9bea9379a8fade01240ee583535fac713b7101400000000000000000000000050016fe5de82d818ab8190b2e32115cae7c0cbf5000000000000000000000000600360f031a22213522329c41ead9f0a4d6f37ff

Arg [0] (address[]) : [0xcb9539df5a8617c4b39105f3d3a614ab72b94f5b, 0x4cc3a18209517a09f472639551357db80a17412b, 0xc9bea9379a8fade01240ee583535fac713b71014, 0x50016fe5de82d818ab8190b2e32115cae7c0cbf5, 0x600360f031a22213522329c41ead9f0a4d6f37ff]

              

Contract source code

// SPDX-License-Identifier: No License (None)
pragma solidity ^0.8.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.0.0, only sets of type `address` (`AddressSet`) and `uint256`
 * (`UintSet`) are supported.
 */
library EnumerableSet {

    struct AddressSet {
        // Storage of set values
        address[] _values;

        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping (address => uint256) _indexes;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        if (!contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) { // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs
            // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.

            address lastvalue = set._values[lastIndex];

            // Move the last value to the index where the value to delete is
            set._values[toDeleteIndex] = lastvalue;
            // Update the index for the moved value
            set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return set._indexes[value] != 0;
    }

    /**
     * @dev Returns 1-based index of value in the set. O(1).
     */
    function indexOf(AddressSet storage set, address value) internal view returns (uint256) {
        return set._indexes[value];
    }


    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return set._values.length;
    }

   /**
    * @dev Returns the value stored at position `index` in the set. O(1).
    *
    * Note that there are no guarantees on the ordering of values inside the
    * array, and it may change when more values are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        require(set._values.length > index, "EnumerableSet: index out of bounds");
        return set._values[index];
    }
}

contract MultisigWallet {
    using EnumerableSet for EnumerableSet.AddressSet;
    struct Ballot {
        uint128 votes;      // bitmap of unique votes (max 127 votes)
        uint64 expire;      // time when ballot expire
        uint8 yea;          // number of votes `Yea`
    }

    EnumerableSet.AddressSet owners; // founders may transfer contract's ownership
    uint256 public ownersSetCounter;   // each time when change owners increase the counter
    uint256 public expirePeriod = 3 days;
    mapping(bytes32 => Ballot) public ballots;
 
    event SetOwner(address owner, bool isEnable);
    event CreateBallot(bytes32 ballotHash, uint256 expired);
    event Execute(bytes32 ballotHash, address to, uint256 value, bytes data);


    modifier onlyThis() {
        require(address(this) == msg.sender, "Only multisig allowed");
        _;
    }
    
    constructor (address[] memory _owners) {
        for (uint i = 0; i < _owners.length; i++) {
            require(_owners[i] != address(0), "Zero address");
            owners.add(_owners[i]);
        }
    }

    // get number of owners
    function getOwnersNumber() external view returns(uint256) {
        return owners.length();
    }

    // returns list of owners addresses
    function getOwners() external view returns(address[] memory) {
        return owners._values;
    }

    // add owner
    function addOwner(address owner) external onlyThis{
        require(owner != address(0), "Zero address");
        require(owners.length() < 127, "Too many owners");
        require(owners.add(owner), "Owner already added");
        ownersSetCounter++; // change owners set
        emit SetOwner(owner, true);
    }

    // remove owner
    function removeOwner(address owner) external onlyThis{
        require(owners.length() > 1, "Remove all owners is not allowed");
        require(owners.remove(owner), "Owner does not exist");
        ownersSetCounter++; // change owners set
        emit SetOwner(owner, false);
    }
    
    function setExpirePeriod(uint256 period) external onlyThis {
        require(period >= 1 days, "Too short period");  // avoid deadlock in case of set too short period
        expirePeriod = period;
    }

    function vote(address to, uint256 value, bytes calldata data) external {
        uint256 index = owners.indexOf(msg.sender);
        require(index != 0, "Only owner");
        bytes32 ballotHash = keccak256(abi.encodePacked(to, value, data, ownersSetCounter));
        Ballot memory b = ballots[ballotHash];
        if (b.expire == 0 || b.expire < uint64(block.timestamp)) { // if no ballot or ballot expired - create new ballot
            b.expire = uint64(block.timestamp + expirePeriod);
            b.votes = 0;
            b.yea = 0;
            emit CreateBallot(ballotHash, b.expire);
        }
        uint256 mask = 1 << index;
        if (b.votes & mask == 0) {  // this owner don't vote yet.
            b.votes = uint128(b.votes | mask); // record owner's vote
            b.yea += 1; // increase total votes "Yea"
        }

        if (b.yea >= owners.length() / 2 + 1) {   // vote "Yea" > 50% of owners
            delete ballots[ballotHash];
            execute(to, value, data);
            emit Execute(ballotHash, to, value, data);
        } else {
            // update ballot
            ballots[ballotHash] = b;
        }
    }

    function execute(address to, uint256 value, bytes memory data) internal {
        (bool success,) = to.call{value: value}(data);
        require(success, "Execute error");
    }

    // allow receive ERC223 tokens
    function tokenReceived(address _from, uint _value, bytes calldata _data) external {} 
    // allow receive ERC721 tokens (NFT)
    function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes calldata _data) external pure returns(bytes4) {
        return bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"));
    }
    // allow receive coin
    receive() external payable {}
}
        

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address[]","name":"_owners","internalType":"address[]"}]},{"type":"event","name":"CreateBallot","inputs":[{"type":"bytes32","name":"ballotHash","internalType":"bytes32","indexed":false},{"type":"uint256","name":"expired","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Execute","inputs":[{"type":"bytes32","name":"ballotHash","internalType":"bytes32","indexed":false},{"type":"address","name":"to","internalType":"address","indexed":false},{"type":"uint256","name":"value","internalType":"uint256","indexed":false},{"type":"bytes","name":"data","internalType":"bytes","indexed":false}],"anonymous":false},{"type":"event","name":"SetOwner","inputs":[{"type":"address","name":"owner","internalType":"address","indexed":false},{"type":"bool","name":"isEnable","internalType":"bool","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"addOwner","inputs":[{"type":"address","name":"owner","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint128","name":"votes","internalType":"uint128"},{"type":"uint64","name":"expire","internalType":"uint64"},{"type":"uint8","name":"yea","internalType":"uint8"}],"name":"ballots","inputs":[{"type":"bytes32","name":"","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"expirePeriod","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address[]","name":"","internalType":"address[]"}],"name":"getOwners","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getOwnersNumber","inputs":[]},{"type":"function","stateMutability":"pure","outputs":[{"type":"bytes4","name":"","internalType":"bytes4"}],"name":"onERC721Received","inputs":[{"type":"address","name":"_operator","internalType":"address"},{"type":"address","name":"_from","internalType":"address"},{"type":"uint256","name":"_tokenId","internalType":"uint256"},{"type":"bytes","name":"_data","internalType":"bytes"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"ownersSetCounter","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"removeOwner","inputs":[{"type":"address","name":"owner","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setExpirePeriod","inputs":[{"type":"uint256","name":"period","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"tokenReceived","inputs":[{"type":"address","name":"_from","internalType":"address"},{"type":"uint256","name":"_value","internalType":"uint256"},{"type":"bytes","name":"_data","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"vote","inputs":[{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"value","internalType":"uint256"},{"type":"bytes","name":"data","internalType":"bytes"}]},{"type":"receive","stateMutability":"payable"}]
            

Deployed ByteCode

0x6080604052600436106100a05760003560e01c80637dd0dd16116100645780637dd0dd161461018b5780638638fb5a1461021057806386cbaed8146102255780638943ec0214610245578063a0e67e2b14610266578063d7a52fa91461028857600080fd5b8063150b7a02146100ac578063163d262f1461010f578063173825d91461013357806321407e53146101555780637065cb481461016b57600080fd5b366100a757005b600080fd5b3480156100b857600080fd5b506100f16100c7366004610ba2565b7f150b7a023d4804d13e8c85fb27262cb750cf6ba9f9dd3bb30d90f482ceeb4b1f95945050505050565b6040516001600160e01b031990911681526020015b60405180910390f35b34801561011b57600080fd5b5061012560025481565b604051908152602001610106565b34801561013f57600080fd5b5061015361014e366004610b80565b6102a8565b005b34801561016157600080fd5b5061012560035481565b34801561017757600080fd5b50610153610186366004610b80565b6103d1565b34801561019757600080fd5b506101df6101a6366004610c6b565b6004602052600090815260409020546001600160801b03811690600160801b810467ffffffffffffffff1690600160c01b900460ff1683565b604080516001600160801b03909416845267ffffffffffffffff909216602084015260ff1690820152606001610106565b34801561021c57600080fd5b50600054610125565b34801561023157600080fd5b50610153610240366004610c11565b610520565b34801561025157600080fd5b50610153610260366004610c11565b50505050565b34801561027257600080fd5b5061027b6107f9565b6040516101069190610cf3565b34801561029457600080fd5b506101536102a3366004610c6b565b61085d565b3033146102d05760405162461bcd60e51b81526004016102c790610d8f565b60405180910390fd5b60016102db60005490565b116103285760405162461bcd60e51b815260206004820181905260248201527f52656d6f766520616c6c206f776e657273206973206e6f7420616c6c6f77656460448201526064016102c7565b61033360008261093b565b6103765760405162461bcd60e51b815260206004820152601460248201527313dddb995c88191bd95cc81b9bdd08195e1a5cdd60621b60448201526064016102c7565b6002805490600061038683610e34565b9091555050604080516001600160a01b0383168152600060208201527f5501576e82029b0850ec7c74ac25b5a46839bf523772a6ac579cc55b092281c891015b60405180910390a150565b3033146103f05760405162461bcd60e51b81526004016102c790610d8f565b6001600160a01b0381166104355760405162461bcd60e51b815260206004820152600c60248201526b5a65726f206164647265737360a01b60448201526064016102c7565b607f61044060005490565b1061047f5760405162461bcd60e51b815260206004820152600f60248201526e546f6f206d616e79206f776e65727360881b60448201526064016102c7565b61048a6000826108c7565b6104cc5760405162461bcd60e51b815260206004820152601360248201527213dddb995c88185b1c9958591e481859191959606a1b60448201526064016102c7565b600280549060006104dc83610e34565b9091555050604080516001600160a01b0383168152600160208201527f5501576e82029b0850ec7c74ac25b5a46839bf523772a6ac579cc55b092281c891016103c6565b336000908152600160205260409020548061056a5760405162461bcd60e51b815260206004820152600a60248201526927b7363c9037bbb732b960b11b60448201526064016102c7565b600085858585600254604051602001610587959493929190610c84565b60408051808303601f190181528282528051602091820120600081815260048352839020606085018452546001600160801b0381168552600160801b810467ffffffffffffffff16928501839052600160c01b900460ff1692840192909252909250158061060c57504267ffffffffffffffff16816020015167ffffffffffffffff16105b156106765760035461061e9042610dbe565b67ffffffffffffffff16602082810182905260008084526040808501919091528051858152918201929092527fdba396e3f86ba92c12e944cda2d787eea52a24f6babcbd54c8ffb2d369bca7c9910160405180910390a15b80516001841b9081166001600160801b03166106ba5781516001600160801b038083169116178252604082018051600191906106b3908390610dd6565b60ff169052505b60026106c560005490565b6106cf9190610dfb565b6106da906001610dbe565b826040015160ff16106107895760008381526004602090815260409182902080546001600160c81b03191690558151601f8801829004820281018201909252868252610745918a918a91908a908a9081908401838280828437600092019190915250610a7d92505050565b7fc75495e0be082158e5855c35d211235055bf668ec3933eb9f3cf757c6493ee6d838989898960405161077c959493929190610d40565b60405180910390a16107ef565b60008381526004602090815260409182902084518154928601519386015160ff16600160c01b0260ff60c01b1967ffffffffffffffff909516600160801b026001600160c01b03199094166001600160801b039092169190911792909217929092161790555b5050505050505050565b60606000800180548060200260200160405190810160405280929190818152602001828054801561085357602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610835575b5050505050905090565b30331461087c5760405162461bcd60e51b81526004016102c790610d8f565b620151808110156108c25760405162461bcd60e51b815260206004820152601060248201526f151bdbc81cda1bdc9d081c195c9a5bd960821b60448201526064016102c7565b600355565b6001600160a01b038116600090815260018301602052604081205461093157508154600180820184556000848152602080822090930180546001600160a01b0319166001600160a01b03861690811790915585549082528286019093526040902091909155610935565b5060005b92915050565b6001600160a01b03811660009081526001830160205260408120548015610a73576000610969600183610e1d565b855490915060009061097d90600190610e1d565b9050600086600001828154811061099657610996610e7b565b60009182526020909120015487546001600160a01b03909116915081908890859081106109c5576109c5610e7b565b600091825260209091200180546001600160a01b0319166001600160a01b03929092169190911790556109f9836001610dbe565b6001600160a01b03821660009081526001890160205260409020558654879080610a2557610a25610e65565b60008281526020808220830160001990810180546001600160a01b03191690559092019092556001600160a01b03881682526001898101909152604082209190915594506109359350505050565b6000915050610935565b6000836001600160a01b03168383604051610a989190610cb8565b60006040518083038185875af1925050503d8060008114610ad5576040519150601f19603f3d011682016040523d82523d6000602084013e610ada565b606091505b50509050806102605760405162461bcd60e51b815260206004820152600d60248201526c22bc32b1baba329032b93937b960991b60448201526064016102c7565b80356001600160a01b0381168114610b3257600080fd5b919050565b60008083601f840112610b4957600080fd5b50813567ffffffffffffffff811115610b6157600080fd5b602083019150836020828501011115610b7957600080fd5b9250929050565b600060208284031215610b9257600080fd5b610b9b82610b1b565b9392505050565b600080600080600060808688031215610bba57600080fd5b610bc386610b1b565b9450610bd160208701610b1b565b935060408601359250606086013567ffffffffffffffff811115610bf457600080fd5b610c0088828901610b37565b969995985093965092949392505050565b60008060008060608587031215610c2757600080fd5b610c3085610b1b565b935060208501359250604085013567ffffffffffffffff811115610c5357600080fd5b610c5f87828801610b37565b95989497509550505050565b600060208284031215610c7d57600080fd5b5035919050565b6bffffffffffffffffffffffff198660601b1681528460148201528284603483013760349201918201526054019392505050565b6000825160005b81811015610cd95760208186018101518583015201610cbf565b81811115610ce8576000828501525b509190910192915050565b6020808252825182820181905260009190848201906040850190845b81811015610d345783516001600160a01b031683529284019291840191600101610d0f565b50909695505050505050565b8581526001600160a01b0385166020820152604081018490526080606082018190528101829052818360a0830137600081830160a090810191909152601f909201601f19160101949350505050565b60208082526015908201527413db9b1e481b5d5b1d1a5cda59c8185b1b1bddd959605a1b604082015260600190565b60008219821115610dd157610dd1610e4f565b500190565b600060ff821660ff84168060ff03821115610df357610df3610e4f565b019392505050565b600082610e1857634e487b7160e01b600052601260045260246000fd5b500490565b600082821015610e2f57610e2f610e4f565b500390565b6000600019821415610e4857610e48610e4f565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052603160045260246000fd5b634e487b7160e01b600052603260045260246000fdfea2646970667358221220202162f5d1bcf784f82920d60cd18e5397c571df146acd6d348f171370019a0a64736f6c63430008070033