Created
December 25, 2021 23:40
-
-
Save z0r0z/b873b8bd10f6eda8715fb22e8d84b10c to your computer and use it in GitHub Desktop.
Simple gas-optimized multi-signature contract with crowdsourced enhancements.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: GPL-3.0-or-later | |
pragma solidity >=0.8.4; | |
/// @notice Simple gas-optimized multi-signature contract. | |
contract Multisig { | |
event Propose(address indexed proposer, uint256 indexed proposal); | |
event Sign(address indexed signer, uint256 indexed proposal); | |
event Execute(uint256 indexed proposal); | |
error NoArrayParity(); | |
error NotSigner(); | |
error Signed(); | |
error InsufficientSigs(); | |
error ExecuteFailed(); | |
uint8 sigsRequired; | |
uint256 proposalCounter; | |
mapping(address => bool) public signer; | |
mapping(uint256 => Proposal) public proposals; | |
mapping(uint256 => mapping(address => bool)) public signed; | |
struct Proposal { | |
address[] targets; | |
uint256[] values; | |
bytes[] payloads; | |
uint8 sigs; | |
} | |
constructor(address[] memory signers_, uint8 sigsRequired_) { | |
// cannot realistically overflow on human timescales | |
unchecked { | |
for (uint256 i; i < signers_.length; i++) { | |
signer[signers_[i]] = true; | |
} | |
} | |
sigsRequired = sigsRequired_; | |
} | |
function getProposal(uint256 proposal) public view virtual returns | |
( address[] memory targets, | |
uint256[] memory values, | |
bytes[] memory payloads, | |
uint8 sigs | |
) | |
{ | |
Proposal storage prop = proposals[proposal]; | |
(targets, values, payloads, sigs) = (prop.targets, prop.values, prop.payloads, prop.sigs); | |
} | |
function propose( | |
address[] calldata targets, | |
uint256[] calldata values, | |
bytes[] calldata payloads | |
) public virtual { | |
if (targets.length != values.length || values.length != payloads.length) revert NoArrayParity(); | |
// cannot realistically overflow on human timescales | |
unchecked { | |
uint256 proposal = proposalCounter++; | |
proposals[proposal] = Proposal({ | |
targets: targets, | |
values: values, | |
payloads: payloads, | |
sigs: 0 | |
}); | |
emit Propose(msg.sender, proposal); | |
} | |
} | |
function sign(uint256 proposal) public virtual { | |
if (!signer[msg.sender]) revert NotSigner(); | |
if (signed[proposal][msg.sender]) revert Signed(); | |
// cannot realistically overflow on human timescales | |
unchecked { | |
proposals[proposal].sigs++; | |
} | |
signed[proposal][msg.sender] = true; | |
emit Sign(msg.sender, proposal); | |
} | |
function execute(uint256 proposal) public virtual { | |
Proposal storage prop = proposals[proposal]; | |
if (prop.sigs < sigsRequired) revert InsufficientSigs(); | |
// cannot realistically overflow on human timescales | |
unchecked { | |
for (uint256 i; i < prop.targets.length; i++) { | |
(bool success, ) = prop.targets[i].call{value: prop.values[i]}(prop.payloads[i]); | |
if (!success) revert ExecuteFailed(); | |
} | |
} | |
delete proposals[proposal]; | |
emit Execute(proposal); | |
} | |
function multicall(bytes[] calldata data) public virtual returns (bytes[] memory results) { | |
results = new bytes[](data.length); | |
// cannot realistically overflow on human timescales | |
unchecked { | |
for (uint256 i = 0; i < data.length; i++) { | |
(bool success, bytes memory result) = address(this).delegatecall(data[i]); | |
if (!success) { | |
if (result.length < 68) revert(); | |
assembly { | |
result := add(result, 0x04) | |
} | |
revert(abi.decode(result, (string))); | |
} | |
results[i] = result; | |
} | |
} | |
} | |
receive() external payable virtual {} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment