HymxBridge

Description:

Multi-signature wallet contract requiring multiple confirmations for transaction execution.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "hymx-bridge.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.3;
pragma abicoder v2;

contract HymxBridge {
    // Event
    event Submission(
        bytes32 indexed id,
        uint256 indexed proposalID,
        bytes32 indexed everHash,
        address owner,
        address to,
        uint256 value,
        bytes data
    );
    event SubmissionFailure(
        bytes32 indexed id,
        uint256 indexed proposalID,
        bytes32 indexed everHash,
        address owner,
        address to,
        uint256 value,
        bytes data
    );
    event Execution(
        bytes32 indexed id,
        uint256 indexed proposalID,
        bytes32 indexed everHash,
        address to,
        uint256 value,
        bytes data
    );
    event ExecutionFailure(
        bytes32 indexed id,
        uint256 indexed proposalID,
        bytes32 indexed everHash,
        address to,
        uint256 value,
        bytes data
    );
    // event Revocation(address indexed sender, bytes32 indexed id); // TODO
    event Deposit(address indexed sender, uint256 value);
    event OwnerAddition(address indexed owner);
    event OwnerRemoval(address indexed owner);
    event RequirementChange(uint256 required);

    event OperatorChange(address indexed operator);
    event PausedChange(bool paused);
    // Event End

    // Storage & Struct
    uint256 public chainID;
    bool public paused;
    address public operator;
    uint256 public required;
    address[] public owners;
    mapping(address => bool) public isOwner;

    mapping(bytes32 => bool) public executed;// tx id => bool
    mapping(bytes32 => mapping(address => bool)) public confirmations;
    // Storage & Struct End

    // Modifier
    modifier validRequirement(uint256 ownerCount, uint256 _required) {
        require(
            ownerCount >= _required && ownerCount != 0 && _required != 0,
            "invalid_required"
        );
        _;
    }

    modifier onlyWallet() {
        require(msg.sender == address(this), "not_wallet");
        _;
    }

    modifier onlyOperator() {
        require(msg.sender == operator, "not_operator");
        _;
    }

    modifier whenNotPaused() {
        require(!paused, "paused");
        _;
    }

    // Modifier End

    // Manage
    function getPaused() public view returns (bool) {
        return paused;
    }

    function getOperator() public view returns (address) {
        return operator;
    }

    function getOwners() public view returns (address[] memory) {
        return owners;
    }

    function getRequire() public view returns (uint256) {
        return required;
    }

    function setOperator(address _operator) public onlyWallet {
        require(_operator != address(0), "null_address");

        operator = _operator;

        emit OperatorChange(operator);
    }

    function setPaused(bool _paused) public onlyOperator {
        paused = _paused;

        emit PausedChange(paused);
    }

    function addOwner(address owner) public onlyWallet {
        require(owner != address(0), "null_address");

        isOwner[owner] = true;
        owners.push(owner);

        emit OwnerAddition(owner);
    }

    function removeOwner(address owner) public onlyWallet {
        require(isOwner[owner], "no_owner_found");

        isOwner[owner] = false;
        for (uint256 i = 0; i < owners.length - 1; i++) {
            if (owners[i] == owner) {
                owners[i] = owners[owners.length - 1];
                break;
            }
        }
        owners.pop();

        if (required > owners.length) {
            changeRequirement(owners.length);
        }

        OwnerRemoval(owner);
    }

    function replaceOwner(address owner, address newOwner) public onlyWallet {
        require(isOwner[owner], "no_owner_found");
        require(newOwner != address(0), "null_address");

        for (uint256 i = 0; i < owners.length; i++) {
            if (owners[i] == owner) {
                owners[i] = newOwner;
                break;
            }
        }
        isOwner[owner] = false;
        isOwner[newOwner] = true;

        OwnerRemoval(owner);
        OwnerAddition(newOwner);
    }

    function changeRequirement(uint256 _required)
        public
        onlyWallet
        validRequirement(owners.length, _required)
    {
        required = _required;
        emit RequirementChange(_required);
    }

    // Manage End

    // Base
    receive() external payable {
        if (msg.value != 0) emit Deposit(msg.sender, msg.value);
    }

    constructor(address[] memory _owners, uint256 _required) validRequirement(_owners.length, _required)
    {
        for (uint256 i = 0; i < _owners.length; i++) {
            isOwner[_owners[i]] = true;
        }

        owners = _owners;
        required = _required;

        uint256 _chainID;
        assembly {
            _chainID := chainid()
        }
        chainID = _chainID;
    }

    function submit(
        uint256 proposalID, // ar tx id
        bytes32 everHash,
        address to,
        uint256 value,
        bytes memory data,
        bytes[] memory sigs
    ) public whenNotPaused returns (bytes32, bool) {
        bytes32 id = txHash(proposalID, everHash, to, value, data);
        require(!executed[id], "tx_executed");

        for (uint256 i = 0; i < sigs.length; i++) {
            address owner = ecAddress(id, sigs[i]);
            if (!isOwner[owner]) {
                emit SubmissionFailure(id, proposalID, everHash, owner, to, value, data);
                continue;
            }

            confirmations[id][owner] = true;
            emit Submission(id, proposalID, everHash, owner, to, value, data);
        }

        if (!isConfirmed(id)) return (id, false);
        executed[id] = true;

        (bool ok, ) = to.call{value: value}(data);
        if (ok) {
            emit Execution(id, proposalID, everHash, to, value, data);
        } else {
            emit ExecutionFailure(id, proposalID, everHash, to, value, data);
        }

        return (id, true);
    }

    // execute multi calls
    function executes(address[] memory tos, uint256[] memory values, bytes[] memory datas) payable public onlyWallet {
        require(tos.length == values.length, "invalid_length");
        require(tos.length == datas.length, "invalid_length");

        for (uint256 i = 0; i < tos.length; i++) {
          (bool ok, ) = tos[i].call{value: values[i]}(datas[i]);
          require(ok, "executed_falied");
        }
    }

    function isConfirmed(bytes32 id) public view returns (bool) {
        uint256 count = 0;
        for (uint256 i = 0; i < owners.length; i++) {
            if (confirmations[id][owners[i]]) count += 1;
            if (count >= required) return true;
        }

        return false;
    }

    // Base End

    // Utils
    function txHash(uint256 proposalID, bytes32 everHash, address to, uint256 value, bytes memory data) public view returns (bytes32) {
        return keccak256(abi.encodePacked(chainID, address(this), proposalID, everHash, to, value, data));
    }

    function ecAddress(bytes32 id, bytes memory sig)
        public
        pure
        returns (address)
    {
        require(sig.length == 65, "invalid_sig_len");

        uint8 v;
        bytes32 r;
        bytes32 s;

        assembly {
            r := mload(add(sig, 0x20))
            s := mload(add(sig, 0x40))
            v := byte(0, mload(add(sig, 0x60)))
        }

        require(v == 27 || v == 28, "invalid_sig_v");

        return
            ecrecover(
                keccak256(
                    abi.encodePacked("\x19Ethereum Signed Message:\
32", id)
                ), v, r, s
            );
    }
    // Utils End
}"
    }
  },
  "settings": {
    "evmVersion": "istanbul",
    "metadata": {
      "bytecodeHash": "ipfs"
    },
    "optimizer": {
      "enabled": false,
      "runs": 200
    },
    "remappings": [],
    "outputSelection": {
      "*": {
        "*": [
          "evm.bytecode",
          "evm.deployedBytecode",
          "devdoc",
          "userdoc",
          "metadata",
          "abi"
        ]
      }
    }
  }
}}

Tags:
Multisig, Multi-Signature, Factory|addr:0x3d9c68e6b4c7105a6ae66c30b204a73f053bd45d|verified:true|block:23730490|tx:0x4503a165f94302d064ff634f911a30d81c12a866d1268978d6448ef3e129e57a|first_check:1762342309

Submitted on: 2025-11-05 12:31:50

Comments

Log in to comment.

No comments yet.