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": []
}
}}
Submitted on: 2025-11-07 12:07:40
Comments
Log in to comment.
No comments yet.