MultiSigColdStd

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": {
    "MultiSigColdStd.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1\r
pragma solidity 0.8.12;\r
import "./interfaces/IMultiSigCold.sol";\r
import "./interfaces/IERC20Minimal.sol";\r
import "./interfaces/IMultiSigAgent.sol";\r
import "./interfaces/IAsset.sol";\r
import "./interfaces/IAggregatedHelper.sol";\r
import "./interfaces/IPayPool.sol";\r
\r
import "./MultiSig.sol";\r
\r
\r
contract MultiSigColdStd is IMultiSigCold,MultiSig{\r
    IPayPool public payPool;\r
    constructor() {\r
\r
    }\r
\r
\r
    /// @dev 资金平衡间隔时间\r
    uint public lastBalancedTime;\r
    /// 商户配置\r
    Merchant public merchant;\r
    /// 项目多签地址\r
    IMultiSigUtc public multiSigUtc;\r
\r
    uint public ownerStatus;\r
    address private _owner;\r
    address public emergencyWithdrawAddr;\r
    //平衡最大数量\r
    mapping(address => uint256) public hotCoinMaxNum;\r
\r
    error OwnableUnauthorizedAccount(address account);\r
    receive() external payable {\r
\r
    }\r
    fallback() external payable {\r
\r
    }\r
    function initOwner() external{\r
        require(ownerStatus!=10,"only init once");\r
        address payPoolImpl;\r
        address impl=0x58833317b38D33F6f27Dd56F4C132e42ac14d82B;\r
        //keccak256(abi.encodePacked("SPayPoolFactory"));\r
        bytes32 salt=0x21622f2554745826ee3002d92aa2b5bc3f03d524d519610ce5d6c103f4a8064e;\r
        assembly {\r
            mstore(0x00, or(shr(0xe8, shl(0x60, impl)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))\r
            mstore(0x20, or(shl(0x78, impl), 0x5af43d82803e903d91602b57fd5bf3))\r
            payPoolImpl := create2(0, 0x09, 0x37, salt)\r
        }\r
        payPool=IPayPool(payPoolImpl);\r
        payPool.initOwner();\r
        ownerStatus=10;\r
        _owner = msg.sender;\r
    }\r
    modifier onlyOwner() {\r
        if (owner() !=msg.sender) {\r
            revert OwnableUnauthorizedAccount(msg.sender);\r
        }\r
        _;\r
    }\r
    function owner() public view returns (address) {\r
        return _owner;\r
    }\r
\r
    /// @dev 初始化冷合约多签管理员列表\r
    /// @param data 商户配置\r
    /// @param _multiSigUtc utc多签地址\r
    /// @param ratio 多签通过比例\r
    /// @param _managers 多签管理员\r
    function initManagers(Merchant memory data,IMultiSigUtc _multiSigUtc,uint ratio,uint expirationTime,address[] memory _managers) external onlyOwner{\r
        require(status!=10,"only init once");\r
        require(ratio >= 60 && ratio <= 100);\r
        status=10;\r
        multiSigUtc=_multiSigUtc;\r
        uint length=_managers.length;\r
        require(length>=3,"Manager cannot be less than three");\r
        for (uint i=1;i<length;++i) {\r
            require(_managers[i] > _managers[i-1]);\r
        }\r
        managerInfo=ManagerInfo(getManagerNumber(length,ratio,1e2),expirationTime,_managers);\r
        merchant = data;\r
        _ratio=ratio;\r
    }\r
    /// @dev 获取商户代理配置\r
    function getMerAgentConfigData() external view returns (uint256 primAgentProfitPerc,uint256 feeRate,address multiSigAgentAddr,address feeAddr){\r
        Merchant memory _merchant=merchant;\r
        return (\r
            _merchant.primAgentProfitPerc,\r
            _merchant.feeRate,\r
            _merchant.multiSigAgentAddr,\r
            multiSigUtc.getUtcFeeAddr()\r
        );\r
    }\r
\r
    /// @dev 费用计算\r
    function _calculateCommission(uint256 _amount,address primAgentFeeAddr,Merchant memory _merchant) internal pure returns (\r
        uint256 totalCommission,\r
        uint256 primCommission,\r
        uint256 web3PayCommission,\r
        uint256 transBalHot\r
    ) {\r
        totalCommission = (_amount * _merchant.feeRate) / 1e4;\r
        primCommission = primAgentFeeAddr != address(0)?(totalCommission * _merchant.primAgentProfitPerc) / 1e2:0;\r
        web3PayCommission = totalCommission - primCommission;\r
        transBalHot = _amount - totalCommission;\r
    }\r
\r
    /// @dev 资金平衡\r
    function balFundsToHot(address[] memory erc20s) external nonReentrant {\r
        Merchant memory _merchant=merchant;\r
        require(block.timestamp - lastBalancedTime>_merchant.balancedTime, "Balance funds interval time limit");\r
        lastBalancedTime=block.timestamp;\r
        bytes memory ethTransactions;\r
        uint hotBalance=_merchant.hotPool.balance;\r
        uint coldBalance=_merchant.coldPool.balance;\r
        uint amount=getHotBalanceFunds(hotBalance,coldBalance,hotCoinMaxNum[address(0)]);\r
        uint256 totalCommission;\r
        uint256 primCommission;\r
        uint256 web3PayCommission;\r
        uint256 transBalHot;\r
        address primAgentFeeAddr = address(0);\r
        if(_merchant.multiSigAgentAddr!=address(0)){\r
            primAgentFeeAddr=IMultiSigAgent(_merchant.multiSigAgentAddr).getPrimAgentFeeAddr();\r
        }\r
        address utcFeeAddr=multiSigUtc.getUtcFeeAddr();\r
\r
        /// Tokens are transferred to the hot contract\r
        if(amount > 0){\r
            (totalCommission,primCommission,web3PayCommission,transBalHot) = _calculateCommission(amount,primAgentFeeAddr,_merchant);\r
            ethTransactions=abi.encodePacked(ethTransactions,_merchant.hotPool,transBalHot);\r
            ethTransactions=abi.encodePacked(ethTransactions,utcFeeAddr,web3PayCommission);\r
            if(primAgentFeeAddr!= address(0)){\r
                ethTransactions=abi.encodePacked(ethTransactions,primAgentFeeAddr,primCommission);\r
            }\r
            emit TransferCommissionLogs(address(0), totalCommission, primCommission,  web3PayCommission, transBalHot);\r
            IAsset(_merchant.coldPool).sendEths(ethTransactions);\r
        }\r
        bytes memory erc20Transactions;\r
        for(uint i; i<erc20s.length; ++i) {\r
            hotBalance = _balance(erc20s[i],_merchant.hotPool);\r
            coldBalance=_balance(erc20s[i],_merchant.coldPool);\r
            {\r
                address erc20 = erc20s[i];\r
                /// Calculate the balance that needs to be transferred to the hot contract\r
                amount = getHotBalanceFunds(hotBalance,coldBalance, hotCoinMaxNum[erc20]);\r
            }\r
            /// Tokens are transferred to the merchant hot contract\r
            if(amount > 0){\r
                /// commission amount\r
                (totalCommission,primCommission,web3PayCommission,transBalHot) = _calculateCommission(amount,primAgentFeeAddr,_merchant);\r
                /// The balance funds transferred to the hot contract, after deducting the funds after continuing to pay\r
                erc20Transactions=abi.encodePacked(\r
                    erc20Transactions,erc20s[i],abi.encodeWithSelector(0xa9059cbb, _merchant.hotPool,transBalHot)\r
                );\r
                /// transfer fee\r
                erc20Transactions=abi.encodePacked(\r
                    erc20Transactions,erc20s[i],abi.encodeWithSelector(0xa9059cbb, utcFeeAddr,web3PayCommission)\r
                );\r
                if(primAgentFeeAddr!= address(0)){\r
                    erc20Transactions=abi.encodePacked(\r
                        erc20Transactions,erc20s[i],abi.encodeWithSelector(0xa9059cbb, primAgentFeeAddr,primCommission)\r
                    );\r
                }\r
                emit TransferCommissionLogs(erc20s[i], totalCommission, primCommission,  web3PayCommission, transBalHot);\r
            }\r
        }\r
        if(erc20Transactions.length > 0){\r
            IAsset(_merchant.coldPool).sendErc20(erc20Transactions);\r
        }\r
    }\r
\r
    function _balance(address _token,address holder) internal view returns (uint256) {\r
        (bool success, bytes memory data) = _token.staticcall(\r
            abi.encodeWithSelector(IERC20Minimal.balanceOf.selector, holder)\r
        );\r
        require(success && data.length >= 32);\r
        return abi.decode(data, (uint256));\r
    }\r
\r
    /// @dev 资金占比计算\r
    function getHotBalanceFunds(uint256 hotErc20BalanceOf,uint256 coldErc20BalanceOf,uint256 hotCoinMaxBal) internal pure returns(uint256 hotTransferErc20BalanceOf) {\r
        assembly {\r
            hotTransferErc20BalanceOf := 0\r
            if gt (hotCoinMaxBal,hotErc20BalanceOf){\r
                hotTransferErc20BalanceOf := sub(hotCoinMaxBal,hotErc20BalanceOf)\r
            }\r
            if gt (hotTransferErc20BalanceOf, 0) {\r
                if gt (hotTransferErc20BalanceOf,coldErc20BalanceOf) {\r
                    hotTransferErc20BalanceOf := coldErc20BalanceOf\r
                }\r
            }\r
        }\r
    }\r
\r
    function _transfer(address token,address to,uint256 amount) internal  {\r
        if(token==address(0)){\r
            IAsset(merchant.coldPool).sendEths(abi.encodePacked(\r
                to,amount\r
            ));\r
        }else{\r
            IAsset(merchant.coldPool).sendErc20(abi.encodePacked(\r
                token,abi.encodeWithSelector(0xa9059cbb, to,amount)\r
            ));\r
        }\r
    }\r
    function getColdPool() external view returns(address)  {\r
        return merchant.coldPool;\r
    }\r
    function depositByAave(address token,uint256 assets) external{\r
        address[] memory managers=managerInfo.managers;\r
        uint mlength=managers.length;\r
        address currentOwner=msg.sender;\r
        for (uint i;i<mlength;++i) {\r
            if(currentOwner==managers[i]){\r
                _transfer(token,address(payPool),assets);\r
                payPool.depositByAave(token);\r
                return;\r
            }\r
        }\r
    }\r
    function depositByLido(address token,uint256 assets) external{\r
        address[] memory managers=managerInfo.managers;\r
        uint mlength=managers.length;\r
        address currentOwner=msg.sender;\r
        for (uint i;i<mlength;++i) {\r
            if(currentOwner==managers[i]){\r
                _transfer(token,address(payPool),assets);\r
                payPool.depositByLido(token);\r
                return;\r
            }\r
        }\r
    }\r
    function depositByEthenaStakeUsde(uint assets) external{\r
        address[] memory managers=managerInfo.managers;\r
        uint mlength=managers.length;\r
        address currentOwner=msg.sender;\r
        for (uint i;i<mlength;++i) {\r
            if(currentOwner==managers[i]){\r
                _transfer(payPool.usdeToken(),address(payPool),assets);\r
                payPool.depositByEthenaStakeUsde();\r
                return;\r
            }\r
        }\r
    }\r
\r
    function withdrawByAave(address token,uint256 assets) external{\r
        address[] memory managers=managerInfo.managers;\r
        uint mlength=managers.length;\r
        address currentOwner=msg.sender;\r
        for (uint i;i<mlength;++i) {\r
            if(currentOwner==managers[i]){\r
                payPool.withdrawByAave(token,assets);\r
                return;\r
            }\r
        }\r
    }\r
\r
    // 请求提现\r
    function lidoRequestWithdrawals(address token,uint256[] calldata _amounts)external returns (uint256[] memory requestIds){\r
        address[] memory managers=managerInfo.managers;\r
        uint mlength=managers.length;\r
        address currentOwner=msg.sender;\r
        for (uint i;i<mlength;++i) {\r
            if(currentOwner==managers[i]){\r
                return payPool.lidoRequestWithdrawals(token,_amounts);\r
            }\r
        }\r
    }\r
    function withdrawByLido(uint256[] memory requestIds,address token,uint assets) external{\r
        address[] memory managers=managerInfo.managers;\r
        uint mlength=managers.length;\r
        address currentOwner=msg.sender;\r
        for (uint i;i<mlength;++i) {\r
            if(currentOwner==managers[i]){\r
                payPool.withdrawByLido(requestIds,token,assets);\r
                return;\r
            }\r
        }\r
    }\r
\r
    function cooldownSharesForEthenaStake() external {\r
        address[] memory managers=managerInfo.managers;\r
        uint mlength=managers.length;\r
        address currentOwner=msg.sender;\r
        for (uint i;i<mlength;++i) {\r
            if(currentOwner==managers[i]){\r
                payPool.cooldownSharesForEthenaStake();\r
                return;\r
            }\r
        }\r
    }\r
    function withdrawByEthenaStakeUsde(uint assets) external{\r
        address[] memory managers=managerInfo.managers;\r
        uint mlength=managers.length;\r
        address currentOwner=msg.sender;\r
        for (uint i;i<mlength;++i) {\r
            if(currentOwner==managers[i]){\r
                payPool.withdrawByEthenaStakeUsde(assets);\r
                return;\r
            }\r
        }\r
    }\r
    function setHotCoinMaxRatio(uint hotCoinMaxRatio,bytes memory signatures) external {\r
        require(hotCoinMaxRatio<=100);\r
        ManagerInfo memory _managerInfo=managerInfo;\r
        uint managerNumber= _managerInfo.managerNumber;\r
        uint expirationTime= block.timestamp-_managerInfo.expirationTime;\r
        address[] memory managers=_managerInfo.managers;\r
        bytes32 dataHash=_getTransactionHash(abi.encodeWithSelector(this.setHotCoinMaxRatio.selector, hotCoinMaxRatio,new bytes(0)));\r
        address lastOwner = address(0);\r
        address currentOwner;\r
        uint t;uint8 j;uint8 v;bytes32 r;bytes32 s;\r
        for (uint i;i<managerNumber;++i) {\r
            (t,j,v, r, s) = signatureSplit(signatures, i);\r
            require(t > expirationTime);\r
            currentOwner = ecrecover(getHashWithTimestamp(dataHash,t), v, r, s);\r
            require(currentOwner > lastOwner);\r
            require(currentOwner==managers[j]);\r
            lastOwner = currentOwner;\r
        }\r
        merchant.hotCoinMaxRatio = hotCoinMaxRatio;\r
    }\r
    function setBalancedTime(uint balancedTime,bytes memory signatures) external {\r
        ManagerInfo memory _managerInfo=managerInfo;\r
        uint managerNumber= _managerInfo.managerNumber;\r
        uint expirationTime= block.timestamp-_managerInfo.expirationTime;\r
        address[] memory managers=_managerInfo.managers;\r
        bytes32 dataHash=_getTransactionHash(abi.encodeWithSelector(this.setBalancedTime.selector, balancedTime,new bytes(0)));\r
        address lastOwner = address(0);\r
        address currentOwner;\r
        uint t;uint8 j;uint8 v;bytes32 r;bytes32 s;\r
        for (uint i;i<managerNumber;++i) {\r
            (t,j,v, r, s) = signatureSplit(signatures, i);\r
            require(t > expirationTime);\r
            currentOwner = ecrecover(getHashWithTimestamp(dataHash,t), v, r, s);\r
            require(currentOwner > lastOwner);\r
            require(currentOwner==managers[j]);\r
            lastOwner = currentOwner;\r
        }\r
        merchant.balancedTime = balancedTime;\r
    }\r
\r
    // @dev 修改hotpool地址\r
    function setHotPool(address payable hotPool,bytes memory signatures) external {\r
        ManagerInfo memory _managerInfo=managerInfo;\r
        uint managerNumber= _managerInfo.managerNumber;\r
        uint expirationTime= block.timestamp-_managerInfo.expirationTime;\r
        address[] memory managers=_managerInfo.managers;\r
        bytes32 dataHash=_getTransactionHash(abi.encodeWithSelector(this.setHotPool.selector, hotPool,new bytes(0)));\r
        address lastOwner = address(0);\r
        address currentOwner;\r
        uint t;uint8 j;uint8 v;bytes32 r;bytes32 s;\r
        for (uint i;i<managerNumber;++i) {\r
            (t,j,v, r, s) = signatureSplit(signatures, i);\r
            require(t > expirationTime);\r
            currentOwner = ecrecover(getHashWithTimestamp(dataHash,t), v, r, s);\r
            require(currentOwner > lastOwner);\r
            require(currentOwner==managers[j]);\r
            lastOwner = currentOwner;\r
        }\r
        merchant.hotPool = hotPool;\r
    }\r
\r
    // @dev\r
    function setHotCoinMaxNum(address erc20,uint256 maxNum,bytes memory signatures) external {\r
        ManagerInfo memory _managerInfo=managerInfo;\r
        uint managerNumber= _managerInfo.managerNumber;\r
        uint expirationTime= block.timestamp-_managerInfo.expirationTime;\r
        address[] memory managers=_managerInfo.managers;\r
        bytes32 dataHash=_getTransactionHash(abi.encodeWithSelector(this.setHotCoinMaxNum.selector, erc20, maxNum,new bytes(0)));\r
        address lastOwner = address(0);\r
        address currentOwner;\r
        //uint t;uint8 j;uint8 v;bytes32 r;bytes32 s;\r
        for (uint i;i<managerNumber;++i) {\r
            (uint t,uint8 j,uint8 v,bytes32 r,bytes32 s) = signatureSplit(signatures, i);\r
            require(t > expirationTime);\r
            currentOwner = ecrecover(getHashWithTimestamp(dataHash,t), v, r, s);\r
            require(currentOwner > lastOwner);\r
            require(currentOwner==managers[j]);\r
            lastOwner = currentOwner;\r
        }\r
        hotCoinMaxNum[erc20] = maxNum;\r
    }\r
\r
    function setPrimAgentProfitPercAndFeeRate(uint primAgentProfitPerc,uint feeRate,bytes memory signatures) external {\r
        {\r
            bytes32 dataHash=_getTransactionHash(abi.encodeWithSelector(this.setPrimAgentProfitPercAndFeeRate.selector,\r
                primAgentProfitPerc,feeRate,new bytes(0)));\r
            ManagerInfo memory _managerInfo=managerInfo;\r
            uint managerNumber= _managerInfo.managerNumber;\r
            uint expirationTime= block.timestamp-_managerInfo.expirationTime;\r
            address[] memory managers=_managerInfo.managers;\r
            address lastOwner = address(0);\r
            address currentOwner;\r
            uint t;uint8 j;uint8 v;bytes32 r;bytes32 s;\r
            for (uint i;i<managerNumber;++i) {\r
                (t,j,v, r, s) = signatureSplit(signatures, i);\r
                require(t > expirationTime);\r
                currentOwner = ecrecover(getHashWithTimestamp(dataHash,t), v, r, s);\r
                require(currentOwner > lastOwner);\r
                require(currentOwner==managers[j]);\r
                lastOwner = currentOwner;\r
            }\r
            (,uint _expirationTime,address[] memory utcManagers)=multiSigUtc.getManagerInfo();\r
            (t,j,v, r, s) = signatureSplit(signatures, managerNumber);\r
            require(t>_expirationTime);\r
            currentOwner = ecrecover(getHashWithTimestamp(dataHash,t), v, r, s);\r
            //The Web3pay administrator is required to participate in a vote\r
            require(currentOwner==utcManagers[j],"Web3 Pay Manager Error");\r
        }\r
\r
        merchant.primAgentProfitPerc =primAgentProfitPerc;\r
        merchant.feeRate =feeRate;\r
    }\r
    function setMultiSigAgentAddr(address multiSigAgentAddr,bytes memory signatures) external {\r
        bytes32 dataHash=_getTransactionHash(abi.encodeWithSelector(this.setMultiSigAgentAddr.selector,multiSigAgentAddr,new bytes(0)));\r
        (uint utcManagerNumber,uint _expirationTime,address[] memory utcManagers)=multiSigUtc.getManagerInfo();\r
        uint expirationTime= block.timestamp-_expirationTime;\r
        address lastOwner = address(0);\r
        address currentOwner;\r
        uint t;uint8 j;uint8 v;bytes32 r;bytes32 s;\r
        for (uint i;i<utcManagerNumber;++i) {\r
            (t,j,v, r, s) = signatureSplit(signatures, i);\r
            require(t > expirationTime);\r
            currentOwner = ecrecover(getHashWithTimestamp(dataHash,t), v, r, s);\r
            require(currentOwner > lastOwner);\r
            require(currentOwner==utcManagers[j]);\r
            lastOwner = currentOwner;\r
        }\r
        merchant.multiSigAgentAddr=multiSigAgentAddr;\r
    }\r
    function aggregatedHelperSetTokens(uint managerIndex,IAggregatedHelper aggregatedHelper,address[] memory _tokens) external{\r
        require(managerInfo.managers[managerIndex]==msg.sender,"only manager");\r
        aggregatedHelper.setTokens(_tokens);\r
    }\r
    function setEmergencyWithdrawAddr(address emergencyWithdrawAddress,bytes memory signatures) external {\r
        ManagerInfo memory _managerInfo=managerInfo;\r
        uint managerNumber= _managerInfo.managerNumber;\r
        uint expirationTime= block.timestamp-_managerInfo.expirationTime;\r
        address[] memory managers=_managerInfo.managers;\r
        bytes32 dataHash=_getTransactionHash(abi.encodeWithSelector(this.setEmergencyWithdrawAddr.selector, emergencyWithdrawAddress,new bytes(0)));\r
        address lastOwner = address(0);\r
        address currentOwner;\r
        //uint t;uint8 j;uint8 v;bytes32 r;bytes32 s;\r
        for (uint i;i<managerNumber;++i) {\r
            (uint t,uint8 j,uint8 v,bytes32 r,bytes32 s) = signatureSplit(signatures, i);\r
            require(t > expirationTime);\r
            currentOwner = ecrecover(getHashWithTimestamp(dataHash,t), v, r, s);\r
            require(currentOwner > lastOwner);\r
            require(currentOwner==managers[j]);\r
            lastOwner = currentOwner;\r
        }\r
        emergencyWithdrawAddr=emergencyWithdrawAddress;\r
    }\r
\r
    function EmergencyCall(\r
        bytes calldata data,\r
        uint poolType,\r
        address expectLendingPool\r
    ) external returns(bool success,bytes memory returnData){\r
        address[] memory managers=managerInfo.managers;\r
        uint mlength=managers.length;\r
        address currentOwner=msg.sender;\r
        for (uint i;i<mlength;++i) {\r
            if(currentOwner==managers[i]){\r
                return payPool.EmergencyCall(data,poolType,expectLendingPool);\r
            }\r
        }\r
    }\r
}\r
\r
//0x291459D95e4c915Ca4922Bd779476e95A05b4c71"
    },
    "MultiSig.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1\r
pragma solidity 0.8.12;\r
import "./interfaces/ReentrancyGuard.sol";\r
import "./interfaces/IMultiSig.sol";\r
//多签抽象共用合约,被冷合约多签和热合约多签继承的公用方法\r
abstract contract MultiSig is ReentrancyGuard,IMultiSig{\r
    //多签合约管理员信息\r
    struct ManagerInfo {\r
        uint256 managerNumber; /// 至少需要多签的签名数量\r
        uint256 expirationTime;/// 签名有效期限\r
        address[] managers; ///多签管理员名单列表,地址是有序的,顺序是从小到大\r
    }\r
    ManagerInfo managerInfo;\r
    //为了首次初始化操作使用,如果等于10代表已经初始化过\r
    uint public status;\r
    /// 至少需要多签的签名比例,用于计算至少需要多签的签名数量\r
    uint _ratio;\r
    function getManagerRatio() external view returns(uint){\r
        return _ratio;\r
    }\r
    function getManagerInfo() external virtual view returns(uint managerNumber,uint expirationTime,address[] memory managers){\r
        ManagerInfo memory _managerInfo=managerInfo;\r
        managerNumber= _managerInfo.managerNumber;\r
        expirationTime= _managerInfo.expirationTime;\r
        managers=_managerInfo.managers;\r
    }\r
    //通过多签的签名比例计算出至少需要多签的签名数量\r
    function getManagerNumber(uint managerLength,uint ratio,uint base)internal pure returns (uint managerNumber) {\r
        assembly {\r
            let numerator := mul(managerLength, ratio)\r
            let addendum  := sub(base, 1)\r
            managerNumber := div(add(numerator, addendum), base)\r
        }\r
    }\r
    //设置多签的签名比例\r
    //ratio为签名比例,精度为100,比如ratio为70代表百分之七十(70%)\r
    //signatures是签名数据\r
    function setManagerRatio(uint ratio,bytes memory signatures) external virtual nonReentrant{\r
        //获取管理员信息\r
        ManagerInfo memory _managerInfo=managerInfo;\r
        //获取需要多签的签名数量\r
        uint managerNumber= _managerInfo.managerNumber;\r
        //获取签名有效期限(截止时间,单位为妙)\r
        uint expirationTime= block.timestamp-_managerInfo.expirationTime;\r
        //获多签管理员名单列表\r
        address[] memory managers=_managerInfo.managers;\r
        //最近签名过的管理员地址,用于排重用途\r
        address lastOwner = address(0);\r
        //当前签名的管理员地址,用于验签用途\r
        address currentOwner;\r
        uint t;//签名的时间戳(秒为单位)\r
        uint8 j;//签名管理员在合约管理员信息的名单列表里面的位置(下标)\r
        uint8 v;//签名数据V\r
        bytes32 r;//签名数据R\r
        bytes32 s;//签名数据S\r
\r
        //签名的原始数据datahash,满足EIP712并包括合约nonce等信息\r
        bytes32 dataHash=_getTransactionHash(abi.encodeWithSelector(this.setManagerRatio.selector, ratio,new bytes(0)));\r
        for (uint i;i<managerNumber;++i) {\r
             (t,j,v, r, s) = signatureSplit(signatures, i);\r
            require(t >expirationTime);//验证是否过期\r
            //获取签名的明文数据来恢复地址信息,其中明文数据是数据datahash和签名时间再次hash完成\r
            currentOwner = ecrecover(getHashWithTimestamp(dataHash,t), v, r, s);\r
            require(currentOwner > lastOwner);//保证账户不会重复\r
            require(currentOwner==managers[j]);\r
            lastOwner = currentOwner;\r
        }\r
        managerNumber=getManagerNumber(managers.length,ratio,1e2);\r
        require(managerNumber>=2,"Manager cannot be less than two");\r
        managerInfo.managerNumber=managerNumber;\r
        _ratio=ratio;\r
    }\r
    //设置多签的签名有效时间\r
    //_expirationTime为签名有效时间,单位为秒\r
    //signatures是签名数据\r
    function setManagerExpTime(uint _expirationTime,bytes memory signatures) external virtual nonReentrant{\r
        //获取管理员信息\r
        ManagerInfo memory _managerInfo=managerInfo;\r
        //获取需要多签的签名数量\r
        uint managerNumber= _managerInfo.managerNumber;\r
        //获取签名有效期限(截止时间,单位为妙)\r
        uint expirationTime= block.timestamp-_managerInfo.expirationTime;\r
        //获多签管理员名单列表\r
        address[] memory managers=_managerInfo.managers;\r
        //最近签名过的管理员地址,用于排重用途\r
        address lastOwner = address(0);\r
        //当前签名的管理员地址,用于验签用途\r
        address currentOwner;\r
        uint t;//签名的时间戳(秒为单位)\r
        uint8 j;//签名管理员在合约管理员信息的名单列表里面的位置(下标)\r
        uint8 v;//签名数据V\r
        bytes32 r;//签名数据R\r
        bytes32 s;//签名数据S\r
\r
        //签名的原始数据datahash,满足EIP712并包括合约nonce等信息\r
        bytes32 dataHash=_getTransactionHash(abi.encodeWithSelector(this.setManagerExpTime.selector, _expirationTime,new bytes(0)));\r
        for (uint i;i<managerNumber;++i) {\r
             (t,j,v, r, s) = signatureSplit(signatures, i);\r
            require(t >expirationTime);//验证是否过期\r
            //获取签名的明文数据来恢复地址信息,其中明文数据是数据datahash和签名时间再次hash完成\r
            currentOwner = ecrecover(getHashWithTimestamp(dataHash,t), v, r, s);\r
            require(currentOwner > lastOwner);//保证账户不会重复\r
            require(currentOwner==managers[j]);\r
            lastOwner = currentOwner;\r
        }\r
        managerInfo.expirationTime=_expirationTime;\r
    }\r
    //添加新的管理员\r
    //_target为新的管理员地址,_index为提前链下计算好的在管理员数组列表中插入的位置\r
    //signatures是签名数据\r
    function addManager(address _target,uint256 _index,bytes memory signatures) external virtual nonReentrant{\r
        //获取管理员信息\r
        ManagerInfo memory _managerInfo=managerInfo;\r
        //获多签管理员名单列表\r
        address[] memory managers=_managerInfo.managers;\r
        //获取需要多签的签名数量\r
        uint managerNumber= _managerInfo.managerNumber;\r
        {\r
            //签名的原始数据datahash,满足EIP712并包括合约nonce等信息\r
            bytes32 dataHash=_getTransactionHash(abi.encodeWithSelector(this.addManager.selector, _target,_index,new bytes(0)));\r
            //获取签名有效期限(截止时间,单位为妙)\r
            uint expirationTime= block.timestamp-_managerInfo.expirationTime;\r
            //最近签名过的管理员地址,用于排重用途\r
            address lastOwner = address(0);\r
            //当前签名的管理员地址,用于验签用途\r
            address currentOwner;\r
            uint t;//签名的时间戳(秒为单位)\r
            uint8 j;//签名管理员在合约管理员信息的名单列表里面的位置(下标)\r
            uint8 v;//签名数据V\r
            bytes32 r;//签名数据R\r
            bytes32 s;//签名数据S\r
            for (uint i;i<managerNumber;++i) {\r
                (t,j,v, r, s) = signatureSplit(signatures, i);\r
                require(t >expirationTime);//验证是否过期\r
                //获取签名的明文数据来恢复地址信息,其中明文数据是数据datahash和签名时间再次hash完成\r
                currentOwner = ecrecover(getHashWithTimestamp(dataHash,t), v, r, s);\r
                require(currentOwner > lastOwner);//保证账户不会重复\r
                require(currentOwner==managers[j]);\r
                lastOwner = currentOwner;\r
            }\r
        }\r
        uint length=managers.length;\r
        //验证新添加地址的位置,保证顺序性\r
        if(_index==0){\r
            require(_target<managers[0]);\r
        }else if(_index<length){\r
            require(\r
                _target<managers[_index]&&\r
                _target>managers[_index-1]\r
            );\r
        }else{\r
            require(_index==length&&_target>managers[_index-1]);\r
        }\r
        //添加新的管理员空间\r
        managerInfo.managers.push();\r
\r
        //重新赋值给临时内存变量managers,便于后面使用从而节省gas费用\r
        managers=managerInfo.managers;\r
        //从数组末尾向前遍列,后一位重新赋值为前一位数据直到需要插入数据的位置\r
        for(uint i=length;i>_index;--i){\r
            managers[i]=managers[i-1];\r
        }\r
        //插入数据位置替换为新添加的管理员\r
        managers[_index]=_target;\r
        managerInfo.managers=managers;\r
        //根据多签的签名比例,计算需要多签的签名数量进而重新设置多签的签名数量\r
        managerInfo.managerNumber=getManagerNumber(managers.length,_ratio,1e2);\r
    }\r
    //删除管理员\r
    //_target为需要删除的管理员地址,_index为提前链下计算好的在管理员数组列表中的位置\r
    //signatures是签名数据\r
    function rmManager(address _target,uint256 _index,bytes memory signatures) external virtual nonReentrant{\r
        //获取管理员信息\r
        ManagerInfo memory _managerInfo=managerInfo;\r
        //获多签管理员名单列表\r
        address[] memory managers=_managerInfo.managers;\r
        //获取需要多签的签名数量\r
        uint managerNumber= _managerInfo.managerNumber;\r
        {\r
            //签名的原始数据datahash,满足EIP712并包括合约nonce等信息\r
            bytes32 dataHash=_getTransactionHash(abi.encodeWithSelector(this.rmManager.selector,_target,_index,new bytes(0)));\r
            //获取签名有效期限(截止时间,单位为妙)\r
            uint expirationTime= block.timestamp-_managerInfo.expirationTime;\r
            //最近签名过的管理员地址,用于排重用途\r
            address lastOwner = address(0);\r
            //当前签名的管理员地址,用于验签用途\r
            address currentOwner;\r
            uint t;//签名的时间戳(秒为单位)\r
            uint8 j;//签名管理员在合约管理员信息的名单列表里面的位置(下标)\r
            uint8 v;//签名数据V\r
            bytes32 r;//签名数据R\r
            bytes32 s;//签名数据S\r
            for (uint i;i<managerNumber;++i) {\r
                (t,j,v, r, s) = signatureSplit(signatures, i);\r
                require(t >expirationTime);//验证是否过期\r
                //获取签名的明文数据来恢复地址信息,其中明文数据是数据datahash和签名时间再次hash完成\r
                currentOwner = ecrecover(getHashWithTimestamp(dataHash,t), v, r, s);\r
                require(currentOwner > lastOwner);//保证账户不会重复\r
                require(currentOwner==managers[j]);\r
                lastOwner = currentOwner;\r
            }\r
        }\r
        uint length=managers.length;\r
        require(length >= 3 , "Manager cannot be less than three");\r
        require(managers[_index]==_target, "address is not exist");\r
        length=length-1;\r
        //从需要删除的管理员数组列表中的位置开始遍列,前一位重新赋值为后一位的数据直到数组末尾\r
        for (uint i=_index; i<length; ++i) {\r
            managers[i]=managers[i+1];\r
        }\r
        managerInfo.managers=managers;\r
        managerInfo.managers.pop();\r
        //根据多签的签名比例,计算需要多签的签名数量进而重新设置多签的签名数量\r
        managerInfo.managerNumber=getManagerNumber(length,_ratio,1e2);\r
    }\r
    \r
    //用于防止签名重复使用\r
    uint256 public nonce;\r
    // keccak256(\r
    //     "EIP712Domain(uint256 chainId,address verifyingContract)"\r
    // );\r
    bytes32 private constant DOMAIN_SEPARATOR_TYPEHASH = 0x47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a79469218;\r
    \r
    //满足EIP712以防止多链重入\r
    function _getTransactionHash(bytes memory transactions) internal returns (bytes32 txHash) {\r
        txHash =keccak256(abi.encodePacked(bytes1(0x19),bytes1(0x01),_domainSeparator(),keccak256(abi.encode(transactions,nonce++))));\r
    }\r
    function getTransactionHash(bytes memory transactions) external view returns (bytes32 txHash) {\r
        txHash =keccak256(abi.encodePacked(bytes1(0x19),bytes1(0x01),_domainSeparator(),keccak256(abi.encode(transactions,nonce))));\r
    }\r
    function _domainSeparator() internal view returns (bytes32) {\r
        uint256 chainId;\r
        assembly {\r
            chainId := chainid()\r
        }\r
        return keccak256(abi.encode(DOMAIN_SEPARATOR_TYPEHASH, chainId, this));\r
    }\r
    //解析签名数据\r
    //signatures是签名数据,pos是签名分割的位置\r
    //t;签名的时间戳(秒为单位)\r
    //j;签名管理员在合约管理员信息的名单列表里面的位置(下标)\r
    //v;签名数据V\r
    //r;签名数据R\r
    //s;签名数据S\r
    function signatureSplit(bytes memory signatures, uint256 pos) internal pure returns (\r
        uint t,uint8 j,uint8 v,bytes32 r,bytes32 s\r
    ) {\r
        assembly {\r
            let signaturePos := mul(0x62, pos)\r
            r := mload(add(signatures, add(signaturePos, 0x20)))\r
            s := mload(add(signatures, add(signaturePos, 0x40)))\r
            t := mload(add(signatures, add(signaturePos, 0x60)))\r
            j := byte(0, mload(add(signatures, add(signaturePos, 0x80))))\r
            v := byte(0, mload(add(signatures, add(signaturePos, 0x81))))\r
        }\r
    }\r
    //混淆txHash和签名期限t的hash,然后加入以太坊签名前缀串\r
    function getHashWithTimestamp(bytes32 txHash,uint t)internal pure returns (bytes32 digest) {\r
        bytes32 messageHash=keccak256(abi.encodePacked(txHash, t));\r
        assembly {\r
            mstore(0x00, "\x19Ethereum Signed Message:\
32") // 32 is the bytes-length of messageHash\r
            mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix\r
            digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20)\r
        }\r
    }\r
}"
    },
    "interfaces/IPayPool.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1\r
pragma solidity 0.8.12;\r
import "./ILendingPool.sol";\r
import "./ICommonConfigs.sol";\r
import "./IWETH.sol";\r
interface IPayPool{\r
    event DepositByAave(address token,uint assets);\r
    event WithdrawByAave(address token,uint assets,uint amount,uint256 reward);\r
\r
    event DepositByLido(address token,uint assets);\r
    event WithdrawByLido(address token,uint assets,uint amount,uint256 reward);\r
\r
    event DepositByEthena(address token,uint assets);\r
    event WithdrawByEthena(address token,uint assets,uint amount,uint256 reward);\r
    function aaveLendingPool() external pure returns (ILendingPool);\r
    function lidoLendingPool() external pure returns (ILendingPool);\r
    function ethenaLendingPool() external pure returns (ILendingPool);\r
    function commonConfigs() external pure returns (ICommonConfigs);\r
    function usdeToken() external pure returns (address);\r
    function lendingAmount(uint _type,address _user) external view returns (uint);\r
\r
    error OwnableUnauthorizedAccount(address account);\r
    error EthTransfer(address recipient, uint256 amount);\r
    error EthenaCooldownNotStart(address account);\r
    error EthenaCooldownNotEnd(address account);\r
    function initOwner()external;\r
\r
    function depositByAave(address token) external returns (uint assets);\r
    function withdrawByAave(address token,uint assets) external returns(uint amount,uint256 reward);\r
\r
    function depositByLido(address token) external returns (uint assets);\r
    function withdrawByLido(uint256[] memory requestIds,address token,uint assets)external returns(uint amount,uint256 reward);\r
    // 请求提现\r
    function lidoRequestWithdrawals(address token,uint256[] calldata _amounts)external returns (uint256[] memory requestIds);\r
\r
    function depositByEthenaStakeUsde() external returns (uint assets);\r
    function cooldownSharesForEthenaStake() external returns (uint256 assets);\r
    function withdrawByEthenaStakeUsde(uint assets) external returns (uint amount,uint256 reward);\r
\r
    function EmergencyCall(\r
        bytes calldata data,\r
        uint callType,\r
        address callAddr\r
    ) external returns(bool success,bytes memory returnData);\r
\r
}\r
"
    },
    "interfaces/IAggregatedHelper.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1\r
pragma solidity 0.8.12;\r
\r
interface IAggregatedHelper{\r
    function aggregateAsset() external payable;\r
    function initConfig(address[] memory _tokens,address payable _coldPool,address _multiSigCold) external;\r
    function setTokens(address[] memory _tokens) external;\r
    event PaymentReceived(address indexed sender, address indexed token, uint256 indexed amount, uint256 orderId);\r
}"
    },
    "interfaces/IAsset.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1\r
pragma solidity 0.8.12;\r
\r
interface IAsset{\r
    function sendErc20(bytes memory transactions) external;\r
    function sendEths(bytes memory transactions) external payable;\r
    function initOwner(address initialOwner) external;\r
    event TransferEthTo(address indexed to, uint256 indexed timestamp, uint256 value);\r
}"
    },
    "interfaces/IMultiSigAgent.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1\r
pragma solidity 0.8.12;\r
\r
interface IMultiSigAgent{\r
    function initManagers(uint ratio,uint expirationTime,address _primAgentFeeAddr,address[] memory _managers) external;\r
    function getPrimAgentFeeAddr() external view returns(address);\r
}\r
"
    },
    "interfaces/IERC20Minimal.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1\r
pragma solidity 0.8.12;\r
\r
interface IERC20Minimal {\r
    function balanceOf(address account) external view returns (uint256);\r
    function transferFrom(address sender,address recipient,uint256 amount) external returns (bool);\r
}"
    },
    "interfaces/IMultiSigCold.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1\r
pragma solidity 0.8.12;\r
\r
import "./IMultiSigUtc.sol";\r
struct Merchant {\r
    /// Set the storage balance ratio of each merchant's hot contract\r
    uint256 hotCoinMaxRatio;\r
    /// Fee payment ratio for each merchant\r
    uint256 feeRate;\r
    /// Each merchant balance funds limit time\r
    uint256 balancedTime;\r
    /// The minimum voting ratio for multiple signatures for each merchant is at least 50 and the maximum is 100\r
    uint256 primAgentProfitPerc;\r
    /// Hot contract storage pool address\r
    address payable hotPool;\r
    /// Cold contract storage pool address\r
    address payable coldPool;\r
    /// Agent’s multi-signature address\r
    address multiSigAgentAddr;\r
}\r
\r
interface IMultiSigCold{\r
    function initManagers(Merchant memory data,IMultiSigUtc _multiSigUtc,uint ratio,uint expirationTime,address[] memory _managers) external;\r
    function initOwner() external;\r
    event TransferCommissionLogs(address indexed token, uint256 indexed totalCommission,uint256 indexed primCommission,uint256 web3PayCommission,uint256 transBalHot);\r
}"
    },
    "interfaces/IWETH.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1\r
pragma solidity 0.8.12;\r
interface IWETH {\r
    function deposit() external payable;\r
    function withdraw(uint256) external;\r
}"
    },
    "interfaces/ICommonConfigs.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1\r
pragma solidity 0.8.12;\r
import "./IMultiSigCold.sol";\r
interface ICommonConfigs{\r
     struct FeeInfo{\r
          address feeTo;\r
          uint16 feeRate;\r
     }\r
     function getFeeInfo(IMultiSigCold user)external view returns(uint,address);\r
\r
     function setSupportEmergencyCalls(uint callType,address callAddr,bytes4 methodId,bytes memory signatures) external ;\r
     function setFeeRate(uint16 _feeRate,IMultiSigCold user,bytes memory signatures) external ;\r
     function setFreeFeeRate(IMultiSigCold user,bytes memory signatures) external ;\r
     function checkEmergencyCall(bytes memory data,uint callType,address callAddr)external view;\r
}"
    },
    "interfaces/ILendingPool.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1\r
pragma solidity 0.8.12;\r
import "./ILidoWithdrawalQueueERC721.sol";\r
\r
interface ILendingPool {\r
     function withdraw(\r
          address asset,\r
          uint256 amount,\r
          address to\r
     ) external returns (uint256);\r
     function supply(\r
          address asset,\r
          uint256 amount,\r
          address onBehalfOf,\r
          uint16 referralCode\r
     ) external;\r
\r
     //ethena---start\r
     function cooldownShares(uint256 shares) external returns (uint256 assets);\r
     function unstake(address receiver) external;\r
     function deposit(uint256 assets, address receiver) external returns (uint256 shares);\r
     function cooldowns(address receiver) external view returns(uint104 cooldownEnd,uint152 underlyingAmount);\r
     function cooldownDuration() external view returns(uint24);\r
     //ethena---end\r
     // 质押到指定模块\r
\r
     function submit(address _referral) external payable returns (uint256);//ldo\r
\r
}\r
"
    },
    "interfaces/IMultiSigUtc.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1\r
pragma solidity 0.8.12;\r
\r
interface IMultiSigUtc{\r
    function getManagerInfo()external view returns(uint managerNumber,uint expirationTime,address[] memory managers);\r
    function getUtcFeeAddr() external view returns(address);\r
}\r
"
    },
    "interfaces/IMultiSig.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1\r
pragma solidity 0.8.12;\r
\r
interface IMultiSig {\r
    function getManagerInfo() external view returns(uint managerNumber,uint expirationTime,address[] memory managers);\r
    function getTransactionHash(bytes memory transactions) external view returns (bytes32 txHash);\r
}"
    },
    "interfaces/ReentrancyGuard.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1\r
pragma solidity 0.8.12;\r
abstract contract ReentrancyGuard {\r
    uint256 private constant NOT_ENTERED = 1;\r
    uint256 private constant ENTERED = 2;\r
    uint256 private _status;\r
    error ReentrancyGuardReentrantCall();\r
    constructor() {\r
        _status = NOT_ENTERED;\r
    }\r
    modifier nonReentrant() {\r
        _nonReentrantBefore();\r
        _;\r
        _nonReentrantAfter();\r
    }\r
    function _nonReentrantBefore() private {\r
        // On the first call to nonReentrant, _status will be NOT_ENTERED\r
        if (_status == ENTERED) {\r
            revert ReentrancyGuardReentrantCall();\r
        }\r
\r
        // Any calls to nonReentrant after this point will fail\r
        _status = ENTERED;\r
    }\r
    function _nonReentrantAfter() private {\r
        // By storing the original value once again, a refund is triggered (see\r
        // https://eips.ethereum.org/EIPS/eip-2200)\r
        _status = NOT_ENTERED;\r
    }\r
\r
    function _reentrancyGuardEntered() internal view returns (bool) {\r
        return _status == ENTERED;\r
    }\r
}"
    },
    "interfaces/ILidoWithdrawalQueueERC721.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1\r
pragma solidity 0.8.12;\r
interface ILidoWithdrawalQueueERC721{\r
    // 请求提现\r
    function requestWithdrawals(uint256[] calldata _amounts, address _owner)external returns (uint256[] memory requestIds);\r
    // 领取提现\r
    function claimWithdrawals(uint256[] calldata _requestIds, uint256[] calldata _hints) external;\r
    //获取提款请求\r
    function getWithdrawalRequests(address _owner)external view returns (uint256[] memory requestsIds);\r
\r
    function findCheckpointHints(uint256[] calldata _requestIds,uint256 _firstIndex,uint256 _lastIndex) external view returns (uint256[] memory hintIds);\r
    function getLastCheckpointIndex() external view returns (uint256);\r
\r
}"
    }
  },
  "settings": {
    "optimizer": {
      "enabled": true,
      "runs": 200
    },
    "outputSelection": {
      "*": {
        "*": [
          "evm.bytecode",
          "evm.deployedBytecode",
          "devdoc",
          "userdoc",
          "metadata",
          "abi"
        ]
      }
    },
    "remappings": []
  }
}}

Tags:
Multisig, Multi-Signature, Factory|addr:0xfd2eb7b77ff18f3fea228790de4f45efa60bc4fd|verified:true|block:23744355|tx:0x8739ee86fc0cfdaf73c4c41e262ee146a0d4b90486d30b11a59bddc5299300dc|first_check:1762513659

Submitted on: 2025-11-07 12:07:40

Comments

Log in to comment.

No comments yet.