solidity/test/libsolidity/syntaxTests/multiSource/safe_contracts.sol

615 lines
20 KiB
Solidity

==== Source: ./contracts/base/Module.sol ====
pragma experimental SMTChecker;
pragma solidity >=0.5.0;
import "../common/SelfAuthorized.sol";
import "./ModuleManager.sol";
contract Module is SelfAuthorized {
ModuleManager public manager;
modifier authorized() override {
require(msg.sender == address(manager), "Method can only be called from manager");
_;
}
}
==== Source: ./contracts/base/ModuleManager.sol ====
pragma experimental SMTChecker;
pragma solidity >=0.5.0;
import "../common/Enum.sol";
import "../common/SelfAuthorized.sol";
import "./Executor.sol";
import "./Module.sol";
contract ModuleManager is SelfAuthorized, Executor {
event EnabledModule(Module module);
event DisabledModule(Module module);
event ExecutionFromModuleSuccess(address indexed module);
event ExecutionFromModuleFailure(address indexed module);
address internal constant SENTINEL_MODULES = address(0x1);
mapping (address => address) internal modules;
function enableModule(Module module)
public
authorized
{
require(address(module) != address(0) && address(module) != SENTINEL_MODULES, "Invalid module address provided");
require(modules[address(module)] == address(0), "Module has already been added");
modules[address(module)] = modules[SENTINEL_MODULES];
modules[SENTINEL_MODULES] = address(module);
emit EnabledModule(module);
}
function disableModule(Module prevModule, Module module)
public
authorized
{
require(address(module) != address(0) && address(module) != SENTINEL_MODULES, "Invalid module address provided");
require(modules[address(prevModule)] == address(module), "Invalid prevModule, module pair provided");
modules[address(prevModule)] = modules[address(module)];
modules[address(module)] = address(0);
emit DisabledModule(module);
}
function execTransactionFromModule(address to, uint256 value, bytes memory data, Enum.Operation operation)
public
returns (bool success)
{
require(msg.sender != SENTINEL_MODULES && modules[msg.sender] != address(0), "Method can only be called from an enabled module");
success = execute(to, value, data, operation, gasleft());
if (success) emit ExecutionFromModuleSuccess(msg.sender);
else emit ExecutionFromModuleFailure(msg.sender);
}
function execTransactionFromModuleReturnData(address to, uint256 value, bytes memory data, Enum.Operation operation)
public
returns (bool success, bytes memory returnData)
{
success = execTransactionFromModule(to, value, data, operation);
assembly {
let ptr := mload(0x40)
mstore(0x40, add(ptr, add(returndatasize(), 0x20)))
mstore(ptr, returndatasize())
returndatacopy(add(ptr, 0x20), 0, returndatasize())
returnData := ptr
}
}
function isModuleEnabled(Module module)
public
view
returns (bool)
{
return SENTINEL_MODULES != address(module) && modules[address(module)] != address(0);
}
function getModulesPaginated(address start, uint256 pageSize)
public
view
returns (address[] memory array, address next)
{
array = new address[](pageSize);
uint256 moduleCount = 0;
address currentModule = modules[start];
while(currentModule != address(0x0) && currentModule != SENTINEL_MODULES && moduleCount < pageSize) {
array[moduleCount] = currentModule;
currentModule = modules[currentModule];
moduleCount++;
}
next = currentModule;
assembly {
mstore(array, moduleCount)
}
}
}
==== Source: ./contracts/base/OwnerManager.sol ====
pragma experimental SMTChecker;
pragma solidity >=0.5.0;
import "../common/SelfAuthorized.sol";
contract OwnerManager is SelfAuthorized {
event AddedOwner(address owner);
event RemovedOwner(address owner);
event ChangedThreshold(uint256 threshold);
address internal constant SENTINEL_OWNERS = address(0x1);
mapping(address => address) internal owners;
uint256 ownerCount;
uint256 internal threshold;
function setupOwners(address[] memory _owners, uint256 _threshold)
internal
{
require(threshold == 0, "Owners have already been setup");
require(_threshold <= _owners.length, "Threshold cannot exceed owner count");
require(_threshold >= 1, "Threshold needs to be greater than 0");
address currentOwner = SENTINEL_OWNERS;
for (uint256 i = 0; i < _owners.length; i++) {
address owner = _owners[i];
require(owner != address(0) && owner != SENTINEL_OWNERS, "Invalid owner address provided");
require(owners[owner] == address(0), "Duplicate owner address provided");
owners[currentOwner] = owner;
currentOwner = owner;
}
owners[currentOwner] = SENTINEL_OWNERS;
ownerCount = _owners.length;
threshold = _threshold;
}
function addOwnerWithThreshold(address owner, uint256 _threshold)
public
authorized
{
require(owner != address(0) && owner != SENTINEL_OWNERS, "Invalid owner address provided");
require(owners[owner] == address(0), "Address is already an owner");
owners[owner] = owners[SENTINEL_OWNERS];
owners[SENTINEL_OWNERS] = owner;
ownerCount++;
emit AddedOwner(owner);
if (threshold != _threshold)
changeThreshold(_threshold);
}
function removeOwner(address prevOwner, address owner, uint256 _threshold)
public
authorized
{
require(ownerCount - 1 >= _threshold, "New owner count needs to be larger than new threshold");
require(owner != address(0) && owner != SENTINEL_OWNERS, "Invalid owner address provided");
require(owners[prevOwner] == owner, "Invalid prevOwner, owner pair provided");
owners[prevOwner] = owners[owner];
owners[owner] = address(0);
ownerCount--;
emit RemovedOwner(owner);
if (threshold != _threshold)
changeThreshold(_threshold);
}
function swapOwner(address prevOwner, address oldOwner, address newOwner)
public
authorized
{
require(newOwner != address(0) && newOwner != SENTINEL_OWNERS, "Invalid owner address provided");
require(owners[newOwner] == address(0), "Address is already an owner");
require(oldOwner != address(0) && oldOwner != SENTINEL_OWNERS, "Invalid owner address provided");
require(owners[prevOwner] == oldOwner, "Invalid prevOwner, owner pair provided");
owners[newOwner] = owners[oldOwner];
owners[prevOwner] = newOwner;
owners[oldOwner] = address(0);
emit RemovedOwner(oldOwner);
emit AddedOwner(newOwner);
}
function changeThreshold(uint256 _threshold)
public
authorized
{
require(_threshold <= ownerCount, "Threshold cannot exceed owner count");
require(_threshold >= 1, "Threshold needs to be greater than 0");
threshold = _threshold;
emit ChangedThreshold(threshold);
}
function getThreshold()
public
view
returns (uint256)
{
return threshold;
}
function isOwner(address owner)
public
view
returns (bool)
{
return owner != SENTINEL_OWNERS && owners[owner] != address(0);
}
function getOwners()
public
view
returns (address[] memory)
{
address[] memory array = new address[](ownerCount);
uint256 index = 0;
address currentOwner = owners[SENTINEL_OWNERS];
while(currentOwner != SENTINEL_OWNERS) {
array[index] = currentOwner;
currentOwner = owners[currentOwner];
index ++;
}
return array;
}
}
==== Source: ./contracts/base/Executor.sol ====
pragma experimental SMTChecker;
pragma solidity >=0.5.0;
import "../common/Enum.sol";
contract Executor {
function execute(address to, uint256 value, bytes memory data, Enum.Operation operation, uint256 txGas)
internal
returns (bool success)
{
if (operation == Enum.Operation.Call)
success = executeCall(to, value, data, txGas);
else if (operation == Enum.Operation.DelegateCall)
success = executeDelegateCall(to, data, txGas);
else
success = false;
}
function executeCall(address to, uint256 value, bytes memory data, uint256 txGas)
internal
returns (bool success)
{
assembly {
success := call(txGas, to, value, add(data, 0x20), mload(data), 0, 0)
}
}
function executeDelegateCall(address to, bytes memory data, uint256 txGas)
internal
returns (bool success)
{
assembly {
success := delegatecall(txGas, to, add(data, 0x20), mload(data), 0, 0)
}
}
}
==== Source: ./contracts/interfaces/ISignatureValidator.sol ====
pragma experimental SMTChecker;
pragma solidity >=0.5.0;
abstract contract ISignatureValidatorConstants {
bytes4 constant internal EIP1271_MAGIC_VALUE = 0x20c13b0b;
}
abstract contract ISignatureValidator is ISignatureValidatorConstants {
function isValidSignature(
bytes memory _data,
bytes memory _signature)
public
view
virtual
returns (bytes4);
}
==== Source: ./contracts/common/SecuredTokenTransfer.sol ====
pragma experimental SMTChecker;
pragma solidity >=0.5.0;
contract SecuredTokenTransfer {
function transferToken (
address token,
address receiver,
uint256 amount
)
internal
returns (bool transferred)
{
bytes memory data = abi.encodeWithSignature("transfer(address,uint256)", receiver, amount);
assembly {
let success := call(sub(gas(), 10000), token, 0, add(data, 0x20), mload(data), 0, 0)
let ptr := mload(0x40)
mstore(0x40, add(ptr, returndatasize()))
returndatacopy(ptr, 0, returndatasize())
switch returndatasize()
case 0 { transferred := success }
case 0x20 { transferred := iszero(or(iszero(success), iszero(mload(ptr)))) }
default { transferred := 0 }
}
}
}
==== Source: ./contracts/common/SelfAuthorized.sol ====
pragma experimental SMTChecker;
pragma solidity >=0.5.0;
contract SelfAuthorized {
modifier authorized() virtual {
require(msg.sender == address(this), "Method can only be called from this contract");
_;
}
}
==== Source: ./contracts/common/SignatureDecoder.sol ====
pragma experimental SMTChecker;
pragma solidity >=0.5.0;
contract SignatureDecoder {
function recoverKey (
bytes32 messageHash,
bytes memory messageSignature,
uint256 pos
)
internal
pure
returns (address)
{
uint8 v;
bytes32 r;
bytes32 s;
(v, r, s) = signatureSplit(messageSignature, pos);
return ecrecover(messageHash, v, r, s);
}
function signatureSplit(bytes memory signatures, uint256 pos)
internal
pure
returns (uint8 v, bytes32 r, bytes32 s)
{
assembly {
let signaturePos := mul(0x41, pos)
r := mload(add(signatures, add(signaturePos, 0x20)))
s := mload(add(signatures, add(signaturePos, 0x40)))
v := and(mload(add(signatures, add(signaturePos, 0x41))), 0xff)
}
}
}
==== Source: ./contracts/common/Enum.sol ====
pragma experimental SMTChecker;
pragma solidity >=0.5.0;
contract Enum {
enum Operation {
Call,
DelegateCall
}
}
==== Source: ./contracts/external/GnosisSafeMath.sol ====
pragma experimental SMTChecker;
pragma solidity >=0.5.0;
library GnosisSafeMath {
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b);
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0); // Solidity only automatically asserts when dividing by 0
uint256 c = a / b;
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a);
uint256 c = a - b;
return c;
}
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a);
return c;
}
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0);
return a % b;
}
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a >= b ? a : b;
}
}
==== Source: ./contracts/GnosisSafe.sol ====
pragma experimental SMTChecker;
pragma solidity >=0.5.0;
import "./base/ModuleManager.sol";
import "./base/OwnerManager.sol";
import "./common/SignatureDecoder.sol";
import "./common/SecuredTokenTransfer.sol";
import "./interfaces/ISignatureValidator.sol";
import "./external/GnosisSafeMath.sol";
contract GnosisSafe
is ModuleManager, OwnerManager, SignatureDecoder, SecuredTokenTransfer, ISignatureValidatorConstants {
using GnosisSafeMath for uint256;
bytes32 private constant SAFE_TX_TYPEHASH = 0xbb8310d486368db6bd6f849402fdd73ad53d316b5a4b2644ad6efe0f941286d8;
bytes32 private constant SAFE_MSG_TYPEHASH = 0x60b3cbf8b4a223d68d641b3b6ddf9a298e7f33710cf3d3a9d1146b5a6150fbca;
event ExecutionFailure(
bytes32 txHash, uint256 payment
);
event ExecutionSuccess(
bytes32 txHash, uint256 payment
);
uint256 public nonce;
bytes32 public domainSeparator;
mapping(bytes32 => uint256) public signedMessages;
mapping(address => mapping(bytes32 => uint256)) public approvedHashes;
function execTransaction(
address to,
uint256 value,
bytes calldata data,
Enum.Operation operation,
uint256 safeTxGas,
uint256 baseGas,
uint256 gasPrice,
address gasToken,
address payable refundReceiver,
bytes calldata signatures
)
external
payable
returns (bool success)
{
bytes32 txHash;
{
bytes memory txHashData = encodeTransactionData(
to, value, data, operation, // Transaction info
safeTxGas, baseGas, gasPrice, gasToken, refundReceiver, // Payment info
nonce
);
nonce++;
txHash = keccak256(txHashData);
checkSignatures(txHash, txHashData, signatures, true);
}
require(gasleft() >= (safeTxGas * 64 / 63).max(safeTxGas + 2500) + 500, "Not enough gas to execute safe transaction");
{
uint256 gasUsed = gasleft();
success = execute(to, value, data, operation, gasPrice == 0 ? (gasleft() - 2500) : safeTxGas);
gasUsed = gasUsed.sub(gasleft());
uint256 payment = 0;
if (gasPrice > 0) {
payment = handlePayment(gasUsed, baseGas, gasPrice, gasToken, refundReceiver);
}
if (success) emit ExecutionSuccess(txHash, payment);
else emit ExecutionFailure(txHash, payment);
}
}
function handlePayment(
uint256 gasUsed,
uint256 baseGas,
uint256 gasPrice,
address gasToken,
address payable refundReceiver
)
private
returns (uint256 payment)
{
address payable receiver = refundReceiver == address(0) ? payable(tx.origin) : refundReceiver;
if (gasToken == address(0)) {
payment = gasUsed.add(baseGas).mul(gasPrice < tx.gasprice ? gasPrice : tx.gasprice);
require(receiver.send(payment), "Could not pay gas costs with ether");
} else {
payment = gasUsed.add(baseGas).mul(gasPrice);
require(transferToken(gasToken, receiver, payment), "Could not pay gas costs with token");
}
}
function checkSignatures(bytes32 dataHash, bytes memory data, bytes memory signatures, bool consumeHash)
internal
{
uint256 _threshold = threshold;
require(_threshold > 0, "Threshold needs to be defined!");
require(signatures.length >= _threshold.mul(65), "Signatures data too short");
address lastOwner = address(0);
address currentOwner;
uint8 v;
bytes32 r;
bytes32 s;
uint256 i;
for (i = 0; i < _threshold; i++) {
(v, r, s) = signatureSplit(signatures, i);
if (v == 0) {
currentOwner = address(uint160(uint256(r)));
require(uint256(s) >= _threshold.mul(65), "Invalid contract signature location: inside static part");
require(uint256(s).add(32) <= signatures.length, "Invalid contract signature location: length not present");
uint256 contractSignatureLen;
assembly {
contractSignatureLen := mload(add(add(signatures, s), 0x20))
}
require(uint256(s).add(32).add(contractSignatureLen) <= signatures.length, "Invalid contract signature location: data not complete");
bytes memory contractSignature;
assembly {
contractSignature := add(add(signatures, s), 0x20)
}
} else if (v == 1) {
currentOwner = address(uint160(uint256(r)));
require(msg.sender == currentOwner || approvedHashes[currentOwner][dataHash] != 0, "Hash has not been approved");
if (consumeHash && msg.sender != currentOwner) {
approvedHashes[currentOwner][dataHash] = 0;
}
} else if (v > 30) {
currentOwner = ecrecover(keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", dataHash)), v - 4, r, s);
} else {
currentOwner = ecrecover(dataHash, v, r, s);
}
require (
currentOwner > lastOwner && owners[currentOwner] != address(0) && currentOwner != SENTINEL_OWNERS,
"Invalid owner provided"
);
lastOwner = currentOwner;
}
}
function isValidSignature(bytes calldata _data, bytes calldata _signature)
external
returns (bytes4)
{
bytes32 messageHash = getMessageHash(_data);
if (_signature.length == 0) {
require(signedMessages[messageHash] != 0, "Hash not approved");
} else {
checkSignatures(messageHash, _data, _signature, false);
}
return EIP1271_MAGIC_VALUE;
}
function getMessageHash(
bytes memory message
)
public
view
returns (bytes32)
{
bytes32 safeMessageHash = keccak256(
abi.encode(SAFE_MSG_TYPEHASH, keccak256(message))
);
return keccak256(
abi.encodePacked(bytes1(0x19), bytes1(0x01), domainSeparator, safeMessageHash)
);
}
function encodeTransactionData(
address to,
uint256 value,
bytes memory data,
Enum.Operation operation,
uint256 safeTxGas,
uint256 baseGas,
uint256 gasPrice,
address gasToken,
address refundReceiver,
uint256 _nonce
)
public
view
returns (bytes memory)
{
bytes32 safeTxHash = keccak256(
abi.encode(SAFE_TX_TYPEHASH, to, value, keccak256(data), operation, safeTxGas, baseGas, gasPrice, gasToken, refundReceiver, _nonce)
);
return abi.encodePacked(bytes1(0x19), bytes1(0x01), domainSeparator, safeTxHash);
}
}