Contract Source Code:
File 1 of 1 : MultiSig
//SPDX-License-Identifier: MIT
pragma solidity ^0.7.3;
pragma experimental SMTChecker;
interface ERC20If {
function balanceOf(address _who) external view returns (uint256);
function transfer(address _to, uint256 _value) external returns (bool);
}
contract MultiSig{
receive() external payable {}
uint256 public nonce = 0;
int private withdrawFlag1 = 0;
int private withdrawFlag2 = 0;
int private withdrawFlag3 = 0;
int private closeFlag1 = 0;
int private closeFlag2 = 0;
int private closeFlag3 = 0;
address[] private owners=new address[](3);
constructor(address _owner1,address _owner2,address _owner3)
{
require(_owner1!=_owner2);
require(_owner1!=_owner3);
require(_owner2!=_owner3);
owners[0] = _owner1;
owners[1] = _owner2;
owners[2] = _owner3;
}
fallback() external payable {}
function getMessageToSignature(address payable[] memory dests, uint256[] memory values) private view returns (bytes memory) {
return abi.encode(nonce, dests, values, this);
}
//测试使用
// function testABIEncode(address[] memory dests, uint256[] memory values,
// uint8 v1, bytes32 r1, bytes32 s1,
// uint8 v2, bytes32 r2, bytes32 s2) public view returns (bytes memory) {
// return abi.encode(dests, values,v1,r1, s1, v2, r2, s2);
//}
function recoverAddress(bytes32 message, uint8 v, bytes32 r, bytes32 s) private pure returns (address) {
return ecrecover(message, v, r, s);
}
function hash(bytes memory data) private pure returns (bytes32) {
return sha256(data);
}
function _validAddress(address addr) private view returns (bool) {
return owners[0] == addr || owners[1] == addr || owners[2] == addr;
}
function spend(address payable[] memory dests, uint256[] memory values,
uint8 v1, bytes32 r1, bytes32 s1,
uint8 v2, bytes32 r2, bytes32 s2) public payable {
require(dests.length > 0 && dests.length == values.length);
uint256 value;
for (uint i = 0; i < values.length; ++i){
value += values[i];
assert(value >= values[i]);
}
require(address(this).balance >= value, "insufficient funds");
bytes32 h=hash((getMessageToSignature(dests, values)));
address addr1=recoverAddress(h, v1, r1, s1);
address addr2=recoverAddress(h, v2, r2, s2);
require(addr1 != addr2, "failed to recover address");
require(_validAddress(addr1), "invalid address");
require(_validAddress(addr2), "invalid address");
nonce = nonce + 1;
for (uint i = 0; i < dests.length; ++i) {
// dests[i].transfer(values[i]);
(bool success, ) =dests[i].call{value:values[i]}("");
require(success, "transfer failed.");
}
}
function withdrawAll() public payable {
require(_validAddress(msg.sender), "invalid address");
if (msg.sender == owners[0]) {
withdrawFlag1 = 1;
}
else if (msg.sender == owners[1]) {
withdrawFlag2 = 1;
}
else if (msg.sender == owners[2]) {
withdrawFlag3 = 1;
}
else {
assert(false);
}
if ((withdrawFlag1+withdrawFlag2+withdrawFlag3)>=2) {
withdrawFlag1 = 0;
withdrawFlag2 = 0;
withdrawFlag3 = 0;
(bool success, ) = msg.sender.call{value:address(this).balance}("");
if (!success) {
revert("withdraw all failed");
}
}
}
function close() public payable {
require(_validAddress(msg.sender), "invalid address");
if (msg.sender == owners[0]) {
closeFlag1 = 1;
}
else if (msg.sender == owners[1]) {
closeFlag2 = 1;
}
else if (msg.sender == owners[2]) {
closeFlag3 = 1;
}
else {
assert(false);
}
if ((closeFlag1+closeFlag2+closeFlag3)>=2){
selfdestruct(msg.sender);
}
}
function transferERC20(address payable[] memory dests, uint256[] memory values,
uint8 v1, bytes32 r1, bytes32 s1,
uint8 v2, bytes32 r2, bytes32 s2, address erc20Token) public {
require(dests.length > 0 && dests.length == values.length);
uint256 value;
for (uint i = 0; i < values.length; ++i){
value += values[i];
assert(value >= values[i]);
}
ERC20If _erc20=(ERC20If)(erc20Token);
require(_erc20.balanceOf(address(this)) >= value, "insufficient ERC20 funds");
require(
verifySignature4ERC20Token(dests,values,v1,r1,s1,v2,r2,s2,erc20Token),
"invalid signature.");
nonce = nonce + 1;
for (uint i = 0; i < dests.length; ++i) {
require(
(_erc20).transfer(dests[i],values[i]),
"transfer erc20Token failed");
}
}
function verifySignature4ERC20Token(address payable[] memory dests, uint256[] memory values,
uint8 v1, bytes32 r1, bytes32 s1,
uint8 v2, bytes32 r2, bytes32 s2, address erc20) private view returns (bool) {
bytes32 h=hash(getMessageToSignature4ERC20Token(dests, values,erc20));
address addr1=recoverAddress(h, v1, r1, s1);
address addr2=recoverAddress(h, v2, r2, s2);
require(addr1 != addr2, "failed to recover address");
require(_validAddress(addr1), "invalid address");
require(_validAddress(addr2), "invalid address");
return true;
}
function getMessageToSignature4ERC20Token(address payable[] memory dests,
uint256[] memory values, address erc20) public view returns (bytes memory) {
return abi.encode(nonce, dests, values, erc20);
}
}