diff --git a/test/libsolidity/syntaxTests/multiSource/safe_contracts.sol b/test/libsolidity/syntaxTests/multiSource/safe_contracts.sol index d7abf0d8e..4d1530131 100644 --- a/test/libsolidity/syntaxTests/multiSource/safe_contracts.sol +++ b/test/libsolidity/syntaxTests/multiSource/safe_contracts.sol @@ -1,372 +1,11 @@ -==== Source: ./contracts/modules/WhitelistModule.sol ==== -pragma experimental SMTChecker; -pragma solidity >=0.5.0; -import "../base/Module.sol"; -import "../base/ModuleManager.sol"; -import "../base/OwnerManager.sol"; -import "../common/Enum.sol"; - - -/// @title Whitelist Module - Allows to execute transactions to whitelisted addresses without confirmations. -/// @author Stefan George - -contract WhitelistModule is Module { - - string public constant NAME = "Whitelist Module"; - string public constant VERSION = "0.1.0"; - - // isWhitelisted mapping maps destination address to boolean. - mapping (address => bool) public isWhitelisted; - - /// @dev Setup function sets initial storage of contract. - /// @param accounts List of whitelisted accounts. - function setup(address[] memory accounts) - public - { - setManager(); - for (uint256 i = 0; i < accounts.length; i++) { - address account = accounts[i]; - require(account != address(0), "Invalid account provided"); - isWhitelisted[account] = true; - } - } - - /// @dev Allows to add destination to whitelist. This can only be done via a Safe transaction. - /// @param account Destination address. - function addToWhitelist(address account) - public - authorized - { - require(account != address(0), "Invalid account provided"); - require(!isWhitelisted[account], "Account is already whitelisted"); - isWhitelisted[account] = true; - } - - /// @dev Allows to remove destination from whitelist. This can only be done via a Safe transaction. - /// @param account Destination address. - function removeFromWhitelist(address account) - public - authorized - { - require(isWhitelisted[account], "Account is not whitelisted"); - isWhitelisted[account] = false; - } - - /// @dev Returns if Safe transaction is to a whitelisted destination. - /// @param to Whitelisted destination address. - /// @param value Not checked. - /// @param data Not checked. - /// @return Returns if transaction can be executed. - function executeWhitelisted(address to, uint256 value, bytes memory data) - public - returns (bool) - { - // Only Safe owners are allowed to execute transactions to whitelisted accounts. - require(OwnerManager(address(manager)).isOwner(msg.sender), "Method can only be called by an owner"); - require(isWhitelisted[to], "Target account is not whitelisted"); - require(manager.execTransactionFromModule(to, value, data, Enum.Operation.Call), "Could not execute transaction"); - } -} -==== Source: ./contracts/modules/DailyLimitModule.sol ==== -pragma experimental SMTChecker; -pragma solidity >=0.5.0; -import "../base/Module.sol"; -import "../base/ModuleManager.sol"; -import "../base/OwnerManager.sol"; -import "../common/Enum.sol"; - - -/// @title Daily Limit Module - Allows to transfer limited amounts of ERC20 tokens and Ether without confirmations. -/// @author Stefan George - -contract DailyLimitModule is Module { - - string public constant NAME = "Daily Limit Module"; - string public constant VERSION = "0.1.0"; - - // dailyLimits mapping maps token address to daily limit settings. - mapping (address => DailyLimit) public dailyLimits; - - struct DailyLimit { - uint256 dailyLimit; - uint256 spentToday; - uint256 lastDay; - } - - /// @dev Setup function sets initial storage of contract. - /// @param tokens List of token addresses. Ether is represented with address 0x0. - /// @param _dailyLimits List of daily limits in smallest units (e.g. Wei for Ether). - function setup(address[] memory tokens, uint256[] memory _dailyLimits) - public - { - setManager(); - for (uint256 i = 0; i < tokens.length; i++) - dailyLimits[tokens[i]].dailyLimit = _dailyLimits[i]; - } - - /// @dev Allows to update the daily limit for a specified token. This can only be done via a Safe transaction. - /// @param token Token contract address. - /// @param dailyLimit Daily limit in smallest token unit. - function changeDailyLimit(address token, uint256 dailyLimit) - public - authorized - { - dailyLimits[token].dailyLimit = dailyLimit; - } - - /// @dev Returns if Safe transaction is a valid daily limit transaction. - /// @param token Address of the token that should be transferred (0 for Ether) - /// @param to Address to which the tokens should be transferred - /// @param amount Amount of tokens (or Ether) that should be transferred - function executeDailyLimit(address token, address to, uint256 amount) - public - { - // Only Safe owners are allowed to execute daily limit transactions. - require(OwnerManager(address(manager)).isOwner(msg.sender), "Method can only be called by an owner"); - require(to != address(0), "Invalid to address provided"); - require(amount > 0, "Invalid amount provided"); - // Validate that transfer is not exceeding daily limit. - require(isUnderLimit(token, amount), "Daily limit has been reached"); - dailyLimits[token].spentToday += amount; - if (token == address(0)) { - require(manager.execTransactionFromModule(to, amount, "", Enum.Operation.Call), "Could not execute ether transfer"); - } else { - bytes memory data = abi.encodeWithSignature("transfer(address,uint256)", to, amount); - require(manager.execTransactionFromModule(token, 0, data, Enum.Operation.Call), "Could not execute token transfer"); - } - } - - function isUnderLimit(address token, uint256 amount) - internal - returns (bool) - { - DailyLimit storage dailyLimit = dailyLimits[token]; - if (today() > dailyLimit.lastDay) { - dailyLimit.lastDay = today(); - dailyLimit.spentToday = 0; - } - if (dailyLimit.spentToday + amount <= dailyLimit.dailyLimit && - dailyLimit.spentToday + amount > dailyLimit.spentToday) - return true; - return false; - } - - /// @dev Returns last midnight as Unix timestamp. - /// @return Unix timestamp. - function today() - public - view - returns (uint) - { - return block.timestamp - (block.timestamp % 1 days); - } -} -==== Source: ./contracts/modules/SocialRecoveryModule.sol ==== -pragma experimental SMTChecker; -pragma solidity >=0.5.0; -import "../base/Module.sol"; -import "../base/ModuleManager.sol"; -import "../base/OwnerManager.sol"; -import "../common/Enum.sol"; - - -/// @title Social Recovery Module - Allows to replace an owner without Safe confirmations if friends approve the replacement. -/// @author Stefan George - -contract SocialRecoveryModule is Module { - - string public constant NAME = "Social Recovery Module"; - string public constant VERSION = "0.1.0"; - - uint256 public threshold; - address[] public friends; - - // isFriend mapping maps friend's address to friend status. - mapping (address => bool) public isFriend; - // isExecuted mapping maps data hash to execution status. - mapping (bytes32 => bool) public isExecuted; - // isConfirmed mapping maps data hash to friend's address to confirmation status. - mapping (bytes32 => mapping (address => bool)) public isConfirmed; - - modifier onlyFriend() { - require(isFriend[msg.sender], "Method can only be called by a friend"); - _; - } - - /// @dev Setup function sets initial storage of contract. - /// @param _friends List of friends' addresses. - /// @param _threshold Required number of friends to confirm replacement. - function setup(address[] memory _friends, uint256 _threshold) - public - { - require(_threshold <= _friends.length, "Threshold cannot exceed friends count"); - require(_threshold >= 2, "At least 2 friends required"); - setManager(); - // Set allowed friends. - for (uint256 i = 0; i < _friends.length; i++) { - address friend = _friends[i]; - require(friend != address(0), "Invalid friend address provided"); - require(!isFriend[friend], "Duplicate friend address provided"); - isFriend[friend] = true; - } - friends = _friends; - threshold = _threshold; - } - - /// @dev Allows a friend to confirm a Safe transaction. - /// @param dataHash Safe transaction hash. - function confirmTransaction(bytes32 dataHash) - public - onlyFriend - { - require(!isExecuted[dataHash], "Recovery already executed"); - isConfirmed[dataHash][msg.sender] = true; - } - - /// @dev Returns if Safe transaction is a valid owner replacement transaction. - /// @param prevOwner Owner that pointed to the owner to be replaced in the linked list - /// @param oldOwner Owner address to be replaced. - /// @param newOwner New owner address. - function recoverAccess(address prevOwner, address oldOwner, address newOwner) - public - onlyFriend - { - bytes memory data = abi.encodeWithSignature("swapOwner(address,address,address)", prevOwner, oldOwner, newOwner); - bytes32 dataHash = getDataHash(data); - require(!isExecuted[dataHash], "Recovery already executed"); - require(isConfirmedByRequiredFriends(dataHash), "Recovery has not enough confirmations"); - isExecuted[dataHash] = true; - require(manager.execTransactionFromModule(address(manager), 0, data, Enum.Operation.Call), "Could not execute recovery"); - } - - /// @dev Returns if Safe transaction is a valid owner replacement transaction. - /// @param dataHash Data hash. - /// @return Confirmation status. - function isConfirmedByRequiredFriends(bytes32 dataHash) - public - view - returns (bool) - { - uint256 confirmationCount; - for (uint256 i = 0; i < friends.length; i++) { - if (isConfirmed[dataHash][friends[i]]) - confirmationCount++; - if (confirmationCount == threshold) - return true; - } - return false; - } - - /// @dev Returns hash of data encoding owner replacement. - /// @param data Data payload. - /// @return Data hash. - function getDataHash(bytes memory data) - public - pure - returns (bytes32) - { - return keccak256(data); - } -} -==== Source: ./contracts/modules/StateChannelModule.sol ==== -pragma experimental SMTChecker; -pragma solidity >=0.5.0; -import "../base/Module.sol"; -import "../base/OwnerManager.sol"; -import "../common/Enum.sol"; -import "../common/SignatureDecoder.sol"; - - -/// @title Gnosis Safe State Module - A module that allows interaction with statechannels. -/// @author Stefan George - -/// @author Richard Meissner - -contract StateChannelModule is Module, SignatureDecoder { - - string public constant NAME = "State Channel Module"; - string public constant VERSION = "0.1.0"; - - // isExecuted mapping allows to check if a transaction (by hash) was already executed. - mapping (bytes32 => uint256) public isExecuted; - - /// @dev Setup function sets manager - function setup() - public - { - setManager(); - } - - /// @dev Allows to execute a Safe transaction confirmed by required number of owners. - /// @param to Destination address of Safe transaction. - /// @param value Ether value of Safe transaction. - /// @param data Data payload of Safe transaction. - /// @param operation Operation type of Safe transaction. - /// @param nonce Nonce used for this Safe transaction. - /// @param signatures Packed signature data ({bytes32 r}{bytes32 s}{uint8 v}) - function execTransaction( - address to, - uint256 value, - bytes memory data, - Enum.Operation operation, - uint256 nonce, - bytes memory signatures - ) - public - { - bytes32 transactionHash = getTransactionHash(to, value, data, operation, nonce); - require(isExecuted[transactionHash] == 0, "Transaction already executed"); - checkHash(transactionHash, signatures); - // Mark as executed and execute transaction. - isExecuted[transactionHash] = 1; - require(manager.execTransactionFromModule(to, value, data, operation), "Could not execute transaction"); - } - - function checkHash(bytes32 transactionHash, bytes memory signatures) - internal - view - { - // There cannot be an owner with address 0. - address lastOwner = address(0); - address currentOwner; - uint256 i; - uint256 threshold = OwnerManager(address(manager)).getThreshold(); - // Validate threshold is reached. - for (i = 0; i < threshold; i++) { - currentOwner = recoverKey(transactionHash, signatures, i); - require(OwnerManager(address(manager)).isOwner(currentOwner), "Signature not provided by owner"); - require(currentOwner > lastOwner, "Signatures are not ordered by owner address"); - lastOwner = currentOwner; - } - } - - /// @dev Returns hash to be signed by owners. - /// @param to Destination address. - /// @param value Ether value. - /// @param data Data payload. - /// @param operation Operation type. - /// @param nonce Transaction nonce. - /// @return Transaction hash. - function getTransactionHash( - address to, - uint256 value, - bytes memory data, - Enum.Operation operation, - uint256 nonce - ) - public - view - returns (bytes32) - { - return keccak256(abi.encodePacked(bytes1(0x19), bytes1(0), this, to, value, data, operation, nonce)); - } -} ==== Source: ./contracts/base/Module.sol ==== pragma experimental SMTChecker; pragma solidity >=0.5.0; -import "../common/MasterCopy.sol"; +import "../common/SelfAuthorized.sol"; import "./ModuleManager.sol"; -/// @title Module - Base class for modules. -/// @author Stefan George - -/// @author Richard Meissner - -contract Module is MasterCopy { +contract Module is SelfAuthorized { ModuleManager public manager; @@ -374,15 +13,6 @@ contract Module is MasterCopy { require(msg.sender == address(manager), "Method can only be called from manager"); _; } - - function setManager() - internal - { - // manager can only be 0 at initialization of contract. - // Check ensures that setup function can only be called once. - require(address(manager) == address(0), "Manager has already been set"); - manager = ModuleManager(msg.sender); - } } ==== Source: ./contracts/base/ModuleManager.sol ==== pragma experimental SMTChecker; @@ -393,9 +23,6 @@ import "./Executor.sol"; import "./Module.sol"; -/// @title Module Manager - A contract that manages modules that can execute transactions via this contract -/// @author Stefan George - -/// @author Richard Meissner - contract ModuleManager is SelfAuthorized, Executor { event EnabledModule(Module module); @@ -407,43 +34,21 @@ contract ModuleManager is SelfAuthorized, Executor { mapping (address => address) internal modules; - function setupModules(address to, bytes memory data) - internal - { - require(modules[SENTINEL_MODULES] == address(0), "Modules have already been initialized"); - modules[SENTINEL_MODULES] = SENTINEL_MODULES; - if (to != address(0)) - // Setup has to complete successfully or transaction fails. - require(executeDelegateCall(to, data, gasleft()), "Could not finish initialization"); - } - - /// @dev Allows to add a module to the whitelist. - /// This can only be done via a Safe transaction. - /// @notice Enables the module `module` for the Safe. - /// @param module Module to be whitelisted. function enableModule(Module module) public authorized { - // Module address cannot be null or sentinel. require(address(module) != address(0) && address(module) != SENTINEL_MODULES, "Invalid module address provided"); - // Module cannot be added twice. 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); } - /// @dev Allows to remove a module from the whitelist. - /// This can only be done via a Safe transaction. - /// @notice Disables the module `module` for the Safe. - /// @param prevModule Module that pointed to the module to be removed in the linked list - /// @param module Module to be removed. function disableModule(Module prevModule, Module module) public authorized { - // Validate module address and check that it corresponds to module index. 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)]; @@ -451,51 +56,30 @@ contract ModuleManager is SelfAuthorized, Executor { emit DisabledModule(module); } - /// @dev Allows a Module to execute a Safe transaction without any further confirmations. - /// @param to Destination address of module transaction. - /// @param value Ether value of module transaction. - /// @param data Data payload of module transaction. - /// @param operation Operation type of module transaction. function execTransactionFromModule(address to, uint256 value, bytes memory data, Enum.Operation operation) public returns (bool success) { - // Only whitelisted modules are allowed. require(msg.sender != SENTINEL_MODULES && modules[msg.sender] != address(0), "Method can only be called from an enabled module"); - // Execute transaction without further confirmations. success = execute(to, value, data, operation, gasleft()); if (success) emit ExecutionFromModuleSuccess(msg.sender); else emit ExecutionFromModuleFailure(msg.sender); } - /// @dev Allows a Module to execute a Safe transaction without any further confirmations and return data - /// @param to Destination address of module transaction. - /// @param value Ether value of module transaction. - /// @param data Data payload of module transaction. - /// @param operation Operation type of module transaction. 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); - // solium-disable-next-line security/no-inline-assembly assembly { - // Load free memory location let ptr := mload(0x40) - // We allocate memory for the return data by setting the free memory location to - // current free memory location + data size + 32 bytes for data size value mstore(0x40, add(ptr, add(returndatasize(), 0x20))) - // Store the size mstore(ptr, returndatasize()) - // Store the data returndatacopy(add(ptr, 0x20), 0, returndatasize()) - // Point the return data to the correct memory location returnData := ptr } } - /// @dev Returns if an module is enabled - /// @return True if the module is enabled function isModuleEnabled(Module module) public view @@ -504,30 +88,13 @@ contract ModuleManager is SelfAuthorized, Executor { return SENTINEL_MODULES != address(module) && modules[address(module)] != address(0); } - /// @dev Returns array of first 10 modules. - /// @return Array of modules. - function getModules() - public - view - returns (address[] memory) - { - (address[] memory array,) = getModulesPaginated(SENTINEL_MODULES, 10); - return array; - } - - /// @dev Returns array of modules. - /// @param start Start of the page. - /// @param pageSize Maximum number of modules that should be returned. - /// @return array Array of modules. function getModulesPaginated(address start, uint256 pageSize) public view returns (address[] memory array, address next) { - // Init array with max page size array = new address[](pageSize); - // Populate return array uint256 moduleCount = 0; address currentModule = modules[start]; while(currentModule != address(0x0) && currentModule != SENTINEL_MODULES && moduleCount < pageSize) { @@ -536,8 +103,6 @@ contract ModuleManager is SelfAuthorized, Executor { moduleCount++; } next = currentModule; - // Set correct size of returned array - // solium-disable-next-line security/no-inline-assembly assembly { mstore(array, moduleCount) } @@ -548,9 +113,6 @@ pragma experimental SMTChecker; pragma solidity >=0.5.0; import "../common/SelfAuthorized.sol"; -/// @title OwnerManager - Manages a set of owners and a threshold to perform actions. -/// @author Stefan George - -/// @author Richard Meissner - contract OwnerManager is SelfAuthorized { event AddedOwner(address owner); @@ -563,26 +125,16 @@ contract OwnerManager is SelfAuthorized { uint256 ownerCount; uint256 internal threshold; - /// @dev Setup function sets initial storage of contract. - /// @param _owners List of Safe owners. - /// @param _threshold Number of required confirmations for a Safe transaction. function setupOwners(address[] memory _owners, uint256 _threshold) internal { - // Threshold can only be 0 at initialization. - // Check ensures that setup function can only be called once. require(threshold == 0, "Owners have already been setup"); - // Validate that threshold is smaller than number of added owners. require(_threshold <= _owners.length, "Threshold cannot exceed owner count"); - // There has to be at least one Safe owner. require(_threshold >= 1, "Threshold needs to be greater than 0"); - // Initializing Safe owners. address currentOwner = SENTINEL_OWNERS; for (uint256 i = 0; i < _owners.length; i++) { - // Owner address cannot be null. address owner = _owners[i]; require(owner != address(0) && owner != SENTINEL_OWNERS, "Invalid owner address provided"); - // No duplicate owners allowed. require(owners[owner] == address(0), "Duplicate owner address provided"); owners[currentOwner] = owner; currentOwner = owner; @@ -592,67 +144,41 @@ contract OwnerManager is SelfAuthorized { threshold = _threshold; } - /// @dev Allows to add a new owner to the Safe and update the threshold at the same time. - /// This can only be done via a Safe transaction. - /// @notice Adds the owner `owner` to the Safe and updates the threshold to `_threshold`. - /// @param owner New owner address. - /// @param _threshold New threshold. function addOwnerWithThreshold(address owner, uint256 _threshold) public authorized { - // Owner address cannot be null. require(owner != address(0) && owner != SENTINEL_OWNERS, "Invalid owner address provided"); - // No duplicate owners allowed. require(owners[owner] == address(0), "Address is already an owner"); owners[owner] = owners[SENTINEL_OWNERS]; owners[SENTINEL_OWNERS] = owner; ownerCount++; emit AddedOwner(owner); - // Change threshold if threshold was changed. if (threshold != _threshold) changeThreshold(_threshold); } - /// @dev Allows to remove an owner from the Safe and update the threshold at the same time. - /// This can only be done via a Safe transaction. - /// @notice Removes the owner `owner` from the Safe and updates the threshold to `_threshold`. - /// @param prevOwner Owner that pointed to the owner to be removed in the linked list - /// @param owner Owner address to be removed. - /// @param _threshold New threshold. function removeOwner(address prevOwner, address owner, uint256 _threshold) public authorized { - // Only allow to remove an owner, if threshold can still be reached. require(ownerCount - 1 >= _threshold, "New owner count needs to be larger than new threshold"); - // Validate owner address and check that it corresponds to owner index. 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); - // Change threshold if threshold was changed. if (threshold != _threshold) changeThreshold(_threshold); } - /// @dev Allows to swap/replace an owner from the Safe with another address. - /// This can only be done via a Safe transaction. - /// @notice Replaces the owner `oldOwner` in the Safe with `newOwner`. - /// @param prevOwner Owner that pointed to the owner to be replaced in the linked list - /// @param oldOwner Owner address to be replaced. - /// @param newOwner New owner address. function swapOwner(address prevOwner, address oldOwner, address newOwner) public authorized { - // Owner address cannot be null. require(newOwner != address(0) && newOwner != SENTINEL_OWNERS, "Invalid owner address provided"); - // No duplicate owners allowed. require(owners[newOwner] == address(0), "Address is already an owner"); - // Validate oldOwner address and check that it corresponds to owner index. require(oldOwner != address(0) && oldOwner != SENTINEL_OWNERS, "Invalid owner address provided"); require(owners[prevOwner] == oldOwner, "Invalid prevOwner, owner pair provided"); owners[newOwner] = owners[oldOwner]; @@ -662,17 +188,11 @@ contract OwnerManager is SelfAuthorized { emit AddedOwner(newOwner); } - /// @dev Allows to update the number of required confirmations by Safe owners. - /// This can only be done via a Safe transaction. - /// @notice Changes the threshold of the Safe to `_threshold`. - /// @param _threshold New threshold. function changeThreshold(uint256 _threshold) public authorized { - // Validate that threshold is smaller than number of owners. require(_threshold <= ownerCount, "Threshold cannot exceed owner count"); - // There has to be at least one Safe owner. require(_threshold >= 1, "Threshold needs to be greater than 0"); threshold = _threshold; emit ChangedThreshold(threshold); @@ -694,8 +214,6 @@ contract OwnerManager is SelfAuthorized { return owner != SENTINEL_OWNERS && owners[owner] != address(0); } - /// @dev Returns array of owners. - /// @return Array of Safe owners. function getOwners() public view @@ -703,7 +221,6 @@ contract OwnerManager is SelfAuthorized { { address[] memory array = new address[](ownerCount); - // populate return array uint256 index = 0; address currentOwner = owners[SENTINEL_OWNERS]; while(currentOwner != SENTINEL_OWNERS) { @@ -720,8 +237,6 @@ pragma solidity >=0.5.0; import "../common/Enum.sol"; -/// @title Executor - A contract that can execute transactions -/// @author Richard Meissner - contract Executor { function execute(address to, uint256 value, bytes memory data, Enum.Operation operation, uint256 txGas) @@ -740,7 +255,6 @@ contract Executor { internal returns (bool success) { - // solium-disable-next-line security/no-inline-assembly assembly { success := call(txGas, to, value, add(data, 0x20), mload(data), 0, 0) } @@ -750,123 +264,21 @@ contract Executor { internal returns (bool success) { - // solium-disable-next-line security/no-inline-assembly assembly { success := delegatecall(txGas, to, add(data, 0x20), mload(data), 0, 0) } } } -==== Source: ./contracts/base/FallbackManager.sol ==== -pragma experimental SMTChecker; -pragma solidity >=0.5.0; - -import "../common/SelfAuthorized.sol"; - -/// @title Fallback Manager - A contract that manages fallback calls made to this contract -/// @author Richard Meissner - -contract FallbackManager is SelfAuthorized { - - // keccak256("fallback_manager.handler.address") - bytes32 internal constant FALLBACK_HANDLER_STORAGE_SLOT = 0x6c9a6c4a39284e37ed1cf53d337577d14212a4870fb976a4366c693b939918d5; - - function internalSetFallbackHandler(address handler) internal { - bytes32 slot = FALLBACK_HANDLER_STORAGE_SLOT; - // solium-disable-next-line security/no-inline-assembly - assembly { - sstore(slot, handler) - } - } - - /// @dev Allows to add a contract to handle fallback calls. - /// Only fallback calls without value and with data will be forwarded. - /// This can only be done via a Safe transaction. - /// @param handler contract to handle fallbacks calls. - function setFallbackHandler(address handler) - public - authorized - { - internalSetFallbackHandler(handler); - } - - fallback() - external - payable - { - // Only calls without value and with data will be forwarded - if (msg.value > 0 || msg.data.length == 0) { - return; - } - bytes32 slot = FALLBACK_HANDLER_STORAGE_SLOT; - address handler; - // solium-disable-next-line security/no-inline-assembly - assembly { - handler := sload(slot) - } - - if (handler != address(0)) { - // solium-disable-next-line security/no-inline-assembly - assembly { - calldatacopy(0, 0, calldatasize()) - let success := call(gas(), handler, 0, 0, calldatasize(), 0, 0) - returndatacopy(0, 0, returndatasize()) - if eq(success, 0) { revert(0, returndatasize()) } - return(0, returndatasize()) - } - } - } -} -==== Source: ./contracts/Migrations.sol ==== -pragma experimental SMTChecker; -pragma solidity >=0.5.0; - -contract Migrations { - address public owner; - uint public last_completed_migration; - - modifier restricted() { - if (msg.sender == owner) _; - } - - constructor() - { - owner = msg.sender; - } - - function setCompleted(uint completed) - public - restricted - { - last_completed_migration = completed; - } - - function upgrade(address new_address) - public - restricted - { - Migrations upgraded = Migrations(new_address); - upgraded.setCompleted(last_completed_migration); - } -} ==== Source: ./contracts/interfaces/ISignatureValidator.sol ==== pragma experimental SMTChecker; pragma solidity >=0.5.0; abstract contract ISignatureValidatorConstants { - // bytes4(keccak256("isValidSignature(bytes,bytes)") bytes4 constant internal EIP1271_MAGIC_VALUE = 0x20c13b0b; } abstract contract ISignatureValidator is ISignatureValidatorConstants { - /** - * @dev Should return whether the signature provided is valid for the provided data - * @param _data Arbitrary length data signed on the behalf of address(this) - * @param _signature Signature byte array associated with _data - * - * MUST return the bytes4 magic value 0x20c13b0b when function passes. - * MUST NOT modify state (using STATICCALL for solc < 0.5, view modifier for solc > 0.5) - * MUST allow external calls - */ function isValidSignature( bytes memory _data, bytes memory _signature) @@ -875,242 +287,13 @@ abstract contract ISignatureValidator is ISignatureValidatorConstants { virtual returns (bytes4); } -==== Source: ./contracts/interfaces/ERC1155TokenReceiver.sol ==== -pragma experimental SMTChecker; -pragma solidity >=0.5.0; - -/** - Note: The ERC-165 identifier for this interface is 0x4e2312e0. -*/ -interface ERC1155TokenReceiver { - /** - @notice Handle the receipt of a single ERC1155 token type. - @dev An ERC1155-compliant smart contract MUST call this function on the token recipient contract, at the end of a `safeTransferFrom` after the balance has been updated. - This function MUST return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` (i.e. 0xf23a6e61) if it accepts the transfer. - This function MUST revert if it rejects the transfer. - Return of any other value than the prescribed keccak256 generated value MUST result in the transaction being reverted by the caller. - @param _operator The address which initiated the transfer (i.e. msg.sender) - @param _from The address which previously owned the token - @param _id The ID of the token being transferred - @param _value The amount of tokens being transferred - @param _data Additional data with no specified format - @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` - */ - function onERC1155Received(address _operator, address _from, uint256 _id, uint256 _value, bytes calldata _data) external returns(bytes4); - - /** - @notice Handle the receipt of multiple ERC1155 token types. - @dev An ERC1155-compliant smart contract MUST call this function on the token recipient contract, at the end of a `safeBatchTransferFrom` after the balances have been updated. - This function MUST return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` (i.e. 0xbc197c81) if it accepts the transfer(s). - This function MUST revert if it rejects the transfer(s). - Return of any other value than the prescribed keccak256 generated value MUST result in the transaction being reverted by the caller. - @param _operator The address which initiated the batch transfer (i.e. msg.sender) - @param _from The address which previously owned the token - @param _ids An array containing ids of each token being transferred (order and length must match _values array) - @param _values An array containing amounts of each token being transferred (order and length must match _ids array) - @param _data Additional data with no specified format - @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` - */ - function onERC1155BatchReceived(address _operator, address _from, uint256[] calldata _ids, uint256[] calldata _values, bytes calldata _data) external returns(bytes4); -} -==== Source: ./contracts/interfaces/ERC721TokenReceiver.sol ==== -pragma experimental SMTChecker; -pragma solidity >=0.5.0; - -/// @dev Note: the ERC-165 identifier for this interface is 0x150b7a02. -interface ERC721TokenReceiver { - /// @notice Handle the receipt of an NFT - /// @dev The ERC721 smart contract calls this function on the recipient - /// after a `transfer`. This function MAY throw to revert and reject the - /// transfer. Return of other than the magic value MUST result in the - /// transaction being reverted. - /// Note: the contract address is always the message sender. - /// @param _operator The address which called `safeTransferFrom` function - /// @param _from The address which previously owned the token - /// @param _tokenId The NFT identifier which is being transferred - /// @param _data Additional data with no specified format - /// @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` - /// unless throwing - function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes calldata _data) external returns(bytes4); -} -==== Source: ./contracts/interfaces/ERC777TokensRecipient.sol ==== -pragma experimental SMTChecker; -pragma solidity >=0.5.0; - -interface ERC777TokensRecipient { - function tokensReceived( - address operator, - address from, - address to, - uint256 amount, - bytes calldata data, - bytes calldata operatorData - ) external; -} -==== Source: ./contracts/libraries/CreateCall.sol ==== -pragma experimental SMTChecker; -pragma solidity >=0.5.0; - - -/// @title Create Call - Allows to use the different create opcodes to deploy a contract -/// @author Richard Meissner - -contract CreateCall { - event ContractCreation(address newContract); - - function performCreate2(uint256 value, bytes memory deploymentData, bytes32 salt) public returns(address newContract) { - // solium-disable-next-line security/no-inline-assembly - assembly { - newContract := create2(value, add(0x20, deploymentData), mload(deploymentData), salt) - } - require(newContract != address(0), "Could not deploy contract"); - emit ContractCreation(newContract); - } - - function performCreate(uint256 value, bytes memory deploymentData) public returns(address newContract) { - // solium-disable-next-line security/no-inline-assembly - assembly { - newContract := create(value, add(deploymentData, 0x20), mload(deploymentData)) - } - require(newContract != address(0), "Could not deploy contract"); - emit ContractCreation(newContract); - } -} -==== Source: ./contracts/libraries/CreateAndAddModules.sol ==== -pragma experimental SMTChecker; -pragma solidity >=0.5.0; -import "../base/Module.sol"; - - -/// @title Create and Add Modules - Allows to create and add multiple module in one transaction. -/// @author Stefan George - -/// @author Richard Meissner - -contract CreateAndAddModules { - - /// @dev Function required to compile contract. Gnosis Safe function is called instead. - /// @param module Not used. - function enableModule(Module module) - public - { - revert(); - } - - /// @dev Allows to create and add multiple module in one transaction. - /// @param proxyFactory Module proxy factory contract. - /// @param data Modules constructor payload. This is the data for each proxy factory call concatenated. (e.g. ) - function createAndAddModules(address proxyFactory, bytes memory data) - public - { - uint256 length = data.length; - Module module; - uint256 i = 0; - while (i < length) { - // solium-disable-next-line security/no-inline-assembly - assembly { - let createBytesLength := mload(add(0x20, add(data, i))) - let createBytes := add(0x40, add(data, i)) - - let output := mload(0x40) - if eq(delegatecall(gas(), proxyFactory, createBytes, createBytesLength, output, 0x20), 0) { revert(0, 0) } - module := and(mload(output), 0xffffffffffffffffffffffffffffffffffffffff) - - // Data is always padded to 32 bytes - i := add(i, add(0x20, mul(div(add(createBytesLength, 0x1f), 0x20), 0x20))) - } - this.enableModule(module); - } - } -} -==== Source: ./contracts/libraries/MultiSend.sol ==== -pragma experimental SMTChecker; -pragma solidity >=0.5.0; - - -/// @title Multi Send - Allows to batch multiple transactions into one. -/// @author Nick Dodson - -/// @author Gonçalo Sá - -/// @author Stefan George - -/// @author Richard Meissner - -contract MultiSend { - - bytes32 constant private GUARD_VALUE = keccak256("multisend.guard.bytes32"); - - bytes32 guard; - - constructor() { - guard = GUARD_VALUE; - } - - /// @dev Sends multiple transactions and reverts all if one fails. - /// @param transactions Encoded transactions. Each transaction is encoded as a packed bytes of - /// operation as a uint8 with 0 for a call or 1 for a delegatecall (=> 1 byte), - /// to as a address (=> 20 bytes), - /// value as a uint256 (=> 32 bytes), - /// data length as a uint256 (=> 32 bytes), - /// data as bytes. - /// see abi.encodePacked for more information on packed encoding - function multiSend(bytes memory transactions) - public - { - require(guard != GUARD_VALUE, "MultiSend should only be called via delegatecall"); - // solium-disable-next-line security/no-inline-assembly - assembly { - let length := mload(transactions) - let i := 0x20 - for { } lt(i, length) { } { - // First byte of the data is the operation. - // We shift by 248 bits (256 - 8 [operation byte]) it right since mload will always load 32 bytes (a word). - // This will also zero out unused data. - let operation := shr(0xf8, mload(add(transactions, i))) - // We offset the load address by 1 byte (operation byte) - // We shift it right by 96 bits (256 - 160 [20 address bytes]) to right-align the data and zero out unused data. - let to := shr(0x60, mload(add(transactions, add(i, 0x01)))) - // We offset the load address by 21 byte (operation byte + 20 address bytes) - let value := mload(add(transactions, add(i, 0x15))) - // We offset the load address by 53 byte (operation byte + 20 address bytes + 32 value bytes) - let dataLength := mload(add(transactions, add(i, 0x35))) - // We offset the load address by 85 byte (operation byte + 20 address bytes + 32 value bytes + 32 data length bytes) - let data := add(transactions, add(i, 0x55)) - let success := 0 - switch operation - case 0 { success := call(gas(), to, value, data, dataLength, 0, 0) } - case 1 { success := delegatecall(gas(), to, data, dataLength, 0, 0) } - if eq(success, 0) { revert(0, 0) } - // Next entry starts at 85 byte + data length - i := add(i, add(0x55, dataLength)) - } - } - } -} -==== Source: ./contracts/common/EtherPaymentFallback.sol ==== -pragma experimental SMTChecker; -pragma solidity >=0.5.0; - - -/// @title EtherPaymentFallback - A contract that has a fallback to accept ether payments -/// @author Richard Meissner - -contract EtherPaymentFallback { - - /// @dev Receive function accepts Ether transactions. - receive() - external - payable - { - - } -} ==== Source: ./contracts/common/SecuredTokenTransfer.sol ==== pragma experimental SMTChecker; pragma solidity >=0.5.0; -/// @title SecuredTokenTransfer - Secure token transfer -/// @author Richard Meissner - contract SecuredTokenTransfer { - /// @dev Transfers a token and returns if it was a success - /// @param token Token that should be transferred - /// @param receiver Receiver to whom the token should be transferred - /// @param amount The amount of tokens that should be transferred function transferToken ( address token, address receiver, @@ -1120,7 +303,6 @@ contract SecuredTokenTransfer { returns (bool transferred) { bytes memory data = abi.encodeWithSignature("transfer(address,uint256)", receiver, amount); - // solium-disable-next-line security/no-inline-assembly assembly { let success := call(sub(gas(), 10000), token, 0, add(data, 0x20), mload(data), 0, 0) let ptr := mload(0x40) @@ -1133,42 +315,11 @@ contract SecuredTokenTransfer { } } } -==== Source: ./contracts/common/MasterCopy.sol ==== -pragma experimental SMTChecker; -pragma solidity >=0.5.0; -import "./SelfAuthorized.sol"; - - -/// @title MasterCopy - Base for master copy contracts (should always be first super contract) -/// This contract is tightly coupled to our proxy contract (see `proxies/GnosisSafeProxy.sol`) -/// @author Richard Meissner - -contract MasterCopy is SelfAuthorized { - - event ChangedMasterCopy(address masterCopy); - - // masterCopy always needs to be first declared variable, to ensure that it is at the same location as in the Proxy contract. - // It should also always be ensured that the address is stored alone (uses a full word) - address private masterCopy; - - /// @dev Allows to upgrade the contract. This can only be done via a Safe transaction. - /// @param _masterCopy New contract address. - function changeMasterCopy(address _masterCopy) - public - authorized - { - // Master copy address cannot be null. - require(_masterCopy != address(0), "Invalid master copy address provided"); - masterCopy = _masterCopy; - emit ChangedMasterCopy(_masterCopy); - } -} ==== Source: ./contracts/common/SelfAuthorized.sol ==== pragma experimental SMTChecker; pragma solidity >=0.5.0; -/// @title SelfAuthorized - authorizes current contract to perform actions -/// @author Richard Meissner - contract SelfAuthorized { modifier authorized() virtual { require(msg.sender == address(this), "Method can only be called from this contract"); @@ -1180,15 +331,8 @@ pragma experimental SMTChecker; pragma solidity >=0.5.0; -/// @title SignatureDecoder - Decodes signatures that a encoded as bytes -/// @author Ricardo Guilherme Schmidt (Status Research & Development GmbH) -/// @author Richard Meissner - contract SignatureDecoder { - /// @dev Recovers address who signed the message - /// @param messageHash operation ethereum signed message hash - /// @param messageSignature message `txHash` signature - /// @param pos which signature to read function recoverKey ( bytes32 messageHash, bytes memory messageSignature, @@ -1205,28 +349,15 @@ contract SignatureDecoder { return ecrecover(messageHash, v, r, s); } - /// @dev divides bytes signature into `uint8 v, bytes32 r, bytes32 s`. - /// @notice Make sure to perform a bounds check for @param pos, to avoid out of bounds access on @param signatures - /// @param pos which signature to read. A prior bounds check of this parameter should be performed, to avoid out of bounds access - /// @param signatures concatenated rsv signatures function signatureSplit(bytes memory signatures, uint256 pos) internal pure returns (uint8 v, bytes32 r, bytes32 s) { - // The signature format is a compact form of: - // {bytes32 r}{bytes32 s}{uint8 v} - // Compact means, uint8 is not padded to 32 bytes. - // solium-disable-next-line security/no-inline-assembly assembly { let signaturePos := mul(0x41, pos) r := mload(add(signatures, add(signaturePos, 0x20))) s := mload(add(signatures, add(signaturePos, 0x40))) - // Here we are loading the last 32 bytes, including 31 bytes - // of 's'. There is no 'mload8' to do this. - // - // 'byte' is not working due to the Solidity parser, so lets - // use the second best option, 'and' v := and(mload(add(signatures, add(signaturePos, 0x41))), 0xff) } } @@ -1236,144 +367,19 @@ pragma experimental SMTChecker; pragma solidity >=0.5.0; -/// @title Enum - Collection of enums -/// @author Richard Meissner - contract Enum { enum Operation { Call, DelegateCall } } -==== Source: ./contracts/mocks/Token.sol ==== -pragma experimental SMTChecker; -pragma solidity >=0.5.0; -import "mock-contract/contracts/MockContract.sol"; -abstract contract Token { - function transfer(address _to, uint value) public virtual returns (bool); -} -==== Source: ./contracts/mocks/ERC1155Token.sol ==== -pragma experimental SMTChecker; -pragma solidity >=0.5.0; - -import "../interfaces/ERC1155TokenReceiver.sol"; -import "../external/GnosisSafeMath.sol"; - -contract ERC1155Token { - - using GnosisSafeMath for uint256; - - // Mapping from token ID to owner balances - mapping (uint256 => mapping(address => uint256)) private _balances; - - // Mapping from owner to operator approvals - mapping (address => mapping(address => bool)) private _operatorApprovals; - - /** - @dev Get the specified address' balance for token with specified ID. - @param owner The address of the token holder - @param id ID of the token - @return The owner's balance of the token type requested - */ - function balanceOf(address owner, uint256 id) public view returns (uint256) { - require(owner != address(0), "ERC1155: balance query for the zero address"); - return _balances[id][owner]; - } - - /** - @dev Transfers `value` amount of an `id` from the `from` address to the `to` address specified. - Caller must be approved to manage the tokens being transferred out of the `from` account. - If `to` is a smart contract, will call `onERC1155Received` on `to` and act appropriately. - @param from Source address - @param to Target address - @param id ID of the token type - @param value Transfer amount - @param data Data forwarded to `onERC1155Received` if `to` is a contract receiver - */ - function safeTransferFrom( - address from, - address to, - uint256 id, - uint256 value, - bytes calldata data - ) - external - { - require(to != address(0), "ERC1155: target address must be non-zero"); - require( - from == msg.sender || _operatorApprovals[from][msg.sender] == true, - "ERC1155: need operator approval for 3rd party transfers." - ); - - _balances[id][from] = _balances[id][from].sub(value); - _balances[id][to] = value.add(_balances[id][to]); - - _doSafeTransferAcceptanceCheck(msg.sender, from, to, id, value, data); - } - - /** - * @dev Test function to mint an amount of a token with the given ID - * @param to The address that will own the minted token - * @param id ID of the token to be minted - * @param value Amount of the token to be minted - * @param data Data forwarded to `onERC1155Received` if `to` is a contract receiver - */ - function mint(address to, uint256 id, uint256 value, bytes calldata data) external { - require(to != address(0), "ERC1155: mint to the zero address"); - - _balances[id][to] = value.add(_balances[id][to]); - - _doSafeTransferAcceptanceCheck(msg.sender, address(0), to, id, value, data); - } - - function isContract(address account) internal view returns (bool) { - // This method relies in extcodesize, which returns 0 for contracts in - // construction, since the code is only stored at the end of the - // constructor execution. - - uint256 size; - // solium-disable-next-line security/no-inline-assembly - assembly { size := extcodesize(account) } - return size > 0; - } - - function _doSafeTransferAcceptanceCheck( - address operator, - address from, - address to, - uint256 id, - uint256 value, - bytes memory data - ) - internal - { - if(isContract(to)) { - require( - ERC1155TokenReceiver(to).onERC1155Received(operator, from, id, value, data) == - ERC1155TokenReceiver(to).onERC1155Received.selector, - "ERC1155: got unknown value from onERC1155Received" - ); - } - } -} ==== Source: ./contracts/external/GnosisSafeMath.sol ==== pragma experimental SMTChecker; pragma solidity >=0.5.0; -/** - * @title GnosisSafeMath - * @dev Math operations with safety checks that revert on error - * Renamed from SafeMath to GnosisSafeMath to avoid conflicts - * TODO: remove once open zeppelin update to solc 0.5.0 - */ library GnosisSafeMath { - /** - * @dev Multiplies two numbers, reverts on overflow. - */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { - // Gas optimization: this is cheaper than requiring 'a' not being zero, but the - // benefit is lost if 'b' is also tested. - // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 if (a == 0) { return 0; } @@ -1384,20 +390,13 @@ library GnosisSafeMath { return c; } - /** - * @dev Integer division of two numbers truncating the quotient, reverts on division by zero. - */ 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; - // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } - /** - * @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend). - */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a); uint256 c = a - b; @@ -1405,9 +404,6 @@ library GnosisSafeMath { return c; } - /** - * @dev Adds two numbers, reverts on overflow. - */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a); @@ -1415,287 +411,35 @@ library GnosisSafeMath { return c; } - /** - * @dev Divides two numbers and returns the remainder (unsigned integer modulo), - * reverts when dividing by zero. - */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { require(b != 0); return a % b; } - /** - * @dev Returns the largest of two numbers. - */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a >= b ? a : b; } } -==== Source: ./contracts/proxies/GnosisSafeProxy.sol ==== -pragma experimental SMTChecker; -pragma solidity >=0.5.0; - -/// @title IProxy - Helper interface to access masterCopy of the Proxy on-chain -/// @author Richard Meissner - -interface IProxy { - function masterCopy() external view returns (address); -} - -/// @title GnosisSafeProxy - Generic proxy contract allows to execute all transactions applying the code of a master contract. -/// @author Stefan George - -/// @author Richard Meissner - -contract GnosisSafeProxy { - - // masterCopy always needs to be first declared variable, to ensure that it is at the same location in the contracts to which calls are delegated. - // To reduce deployment costs this variable is internal and needs to be retrieved via `getStorageAt` - address internal masterCopy; - - /// @dev Constructor function sets address of master copy contract. - /// @param _masterCopy Master copy address. - constructor(address _masterCopy) - { - require(_masterCopy != address(0), "Invalid master copy address provided"); - masterCopy = _masterCopy; - } - - /// @dev Fallback function forwards all transactions and returns all received return data. - fallback() - external - payable - { - // solium-disable-next-line security/no-inline-assembly - assembly { - let masterCopy_ := and(sload(0), 0xffffffffffffffffffffffffffffffffffffffff) - // 0xa619486e == keccak("masterCopy()"). The value is right padded to 32-bytes with 0s - if eq(calldataload(0), 0xa619486e00000000000000000000000000000000000000000000000000000000) { - mstore(0, masterCopy_) - return(0, 0x20) - } - calldatacopy(0, 0, calldatasize()) - let success := delegatecall(gas(), masterCopy_, 0, calldatasize(), 0, 0) - returndatacopy(0, 0, returndatasize()) - if eq(success, 0) { revert(0, returndatasize()) } - return(0, returndatasize()) - } - } -} -==== Source: ./contracts/proxies/IProxyCreationCallback.sol ==== -pragma experimental SMTChecker; -pragma solidity >=0.5.3; -import "./GnosisSafeProxy.sol"; - -interface IProxyCreationCallback { - function proxyCreated(GnosisSafeProxy proxy, address _mastercopy, bytes calldata initializer, uint256 saltNonce) external; -} -==== Source: ./contracts/proxies/PayingProxy.sol ==== -pragma experimental SMTChecker; -pragma solidity >=0.5.0; -import "../common/SecuredTokenTransfer.sol"; -import "./DelegateConstructorProxy.sol"; - -/// @title Paying Proxy - Generic proxy contract allows to execute all transactions applying the code of a master contract. It is possible to send along initialization data with the constructor. And sends funds after creation to a specified account. -/// @author Stefan George - -/// @author Richard Meissner - -contract PayingProxy is DelegateConstructorProxy, SecuredTokenTransfer { - - /// @dev Constructor function sets address of master copy contract. - /// @param _masterCopy Master copy address. - /// @param initializer Data used for a delegate call to initialize the contract. - /// @param funder Address that should be paid for the execution of this call - /// @param paymentToken Token that should be used for the payment (0 is ETH) - /// @param payment Value that should be paid - constructor(address _masterCopy, bytes memory initializer, address payable funder, address paymentToken, uint256 payment) - DelegateConstructorProxy(_masterCopy, initializer) - { - if (payment > 0) { - if (paymentToken == address(0)) { - // solium-disable-next-line security/no-send - require(funder.send(payment), "Could not pay safe creation with ether"); - } else { - require(transferToken(paymentToken, funder, payment), "Could not pay safe creation with token"); - } - } - } -} -==== Source: ./contracts/proxies/DelegateConstructorProxy.sol ==== -pragma experimental SMTChecker; -pragma solidity >=0.5.0; -import "./GnosisSafeProxy.sol"; - - -/// @title Delegate Constructor Proxy - Generic proxy contract allows to execute all transactions applying the code of a master contract. It is possible to send along initialization data with the constructor. -/// @author Stefan George - -/// @author Richard Meissner - -contract DelegateConstructorProxy is GnosisSafeProxy { - - /// @dev Constructor function sets address of master copy contract. - /// @param _masterCopy Master copy address. - /// @param initializer Data used for a delegate call to initialize the contract. - constructor(address _masterCopy, bytes memory initializer) GnosisSafeProxy(_masterCopy) - { - if (initializer.length > 0) { - // solium-disable-next-line security/no-inline-assembly - assembly { - let masterCopy_ := and(sload(0), 0xffffffffffffffffffffffffffffffffffffffff) - let success := delegatecall(sub(gas(), 10000), masterCopy_, add(initializer, 0x20), mload(initializer), 0, 0) - let ptr := mload(0x40) - returndatacopy(ptr, 0, returndatasize()) - if eq(success, 0) { revert(ptr, returndatasize()) } - } - } - } -} -==== Source: ./contracts/proxies/GnosisSafeProxyFactory.sol ==== -pragma experimental SMTChecker; -pragma solidity >=0.5.3; -import "./GnosisSafeProxy.sol"; -import "./IProxyCreationCallback.sol"; - -/// @title Proxy Factory - Allows to create new proxy contact and execute a message call to the new proxy within one transaction. -/// @author Stefan George - -contract GnosisSafeProxyFactory { - - event ProxyCreation(GnosisSafeProxy proxy); - - /// @dev Allows to create new proxy contact and execute a message call to the new proxy within one transaction. - /// @param masterCopy Address of master copy. - /// @param data Payload for message call sent to new proxy contract. - function createProxy(address masterCopy, bytes memory data) - public - returns (GnosisSafeProxy proxy) - { - proxy = new GnosisSafeProxy(masterCopy); - if (data.length > 0) - // solium-disable-next-line security/no-inline-assembly - assembly { - if eq(call(gas(), proxy, 0, add(data, 0x20), mload(data), 0, 0), 0) { revert(0, 0) } - } - emit ProxyCreation(proxy); - } - - /// @dev Allows to retrieve the runtime code of a deployed Proxy. This can be used to check that the expected Proxy was deployed. - function proxyRuntimeCode() public pure returns (bytes memory) { - return type(GnosisSafeProxy).runtimeCode; - } - - /// @dev Allows to retrieve the creation code used for the Proxy deployment. With this it is easily possible to calculate predicted address. - function proxyCreationCode() public pure returns (bytes memory) { - return type(GnosisSafeProxy).creationCode; - } - - /// @dev Allows to create new proxy contact using CREATE2 but it doesn't run the initializer. - /// This method is only meant as an utility to be called from other methods - /// @param _mastercopy Address of master copy. - /// @param initializer Payload for message call sent to new proxy contract. - /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract. - function deployProxyWithNonce(address _mastercopy, bytes memory initializer, uint256 saltNonce) - internal - returns (GnosisSafeProxy proxy) - { - // If the initializer changes the proxy address should change too. Hashing the initializer data is cheaper than just concatinating it - bytes32 salt = keccak256(abi.encodePacked(keccak256(initializer), saltNonce)); - bytes memory deploymentData = abi.encodePacked(type(GnosisSafeProxy).creationCode, uint256(uint160(_mastercopy))); - // solium-disable-next-line security/no-inline-assembly - assembly { - proxy := create2(0x0, add(0x20, deploymentData), mload(deploymentData), salt) - } - require(address(proxy) != address(0), "Create2 call failed"); - } - - /// @dev Allows to create new proxy contact and execute a message call to the new proxy within one transaction. - /// @param _mastercopy Address of master copy. - /// @param initializer Payload for message call sent to new proxy contract. - /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract. - function createProxyWithNonce(address _mastercopy, bytes memory initializer, uint256 saltNonce) - public - returns (GnosisSafeProxy proxy) - { - proxy = deployProxyWithNonce(_mastercopy, initializer, saltNonce); - if (initializer.length > 0) - // solium-disable-next-line security/no-inline-assembly - assembly { - if eq(call(gas(), proxy, 0, add(initializer, 0x20), mload(initializer), 0, 0), 0) { revert(0,0) } - } - emit ProxyCreation(proxy); - } - - /// @dev Allows to create new proxy contact, execute a message call to the new proxy and call a specified callback within one transaction - /// @param _mastercopy Address of master copy. - /// @param initializer Payload for message call sent to new proxy contract. - /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract. - /// @param callback Callback that will be invoced after the new proxy contract has been successfully deployed and initialized. - function createProxyWithCallback(address _mastercopy, bytes memory initializer, uint256 saltNonce, IProxyCreationCallback callback) - public - returns (GnosisSafeProxy proxy) - { - uint256 saltNonceWithCallback = uint256(keccak256(abi.encodePacked(saltNonce, callback))); - proxy = createProxyWithNonce(_mastercopy, initializer, saltNonceWithCallback); - if (address(callback) != address(0)) - callback.proxyCreated(proxy, _mastercopy, initializer, saltNonce); - } - - /// @dev Allows to get the address for a new proxy contact created via `createProxyWithNonce` - /// This method is only meant for address calculation purpose when you use an initializer that would revert, - /// therefore the response is returned with a revert. When calling this method set `from` to the address of the proxy factory. - /// @param _mastercopy Address of master copy. - /// @param initializer Payload for message call sent to new proxy contract. - /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract. - function calculateCreateProxyWithNonceAddress(address _mastercopy, bytes calldata initializer, uint256 saltNonce) - external - returns (GnosisSafeProxy proxy) - { - proxy = deployProxyWithNonce(_mastercopy, initializer, saltNonce); - revert(string(abi.encodePacked(proxy))); - } - -} ==== Source: ./contracts/GnosisSafe.sol ==== pragma experimental SMTChecker; pragma solidity >=0.5.0; import "./base/ModuleManager.sol"; import "./base/OwnerManager.sol"; -import "./base/FallbackManager.sol"; -import "./common/MasterCopy.sol"; import "./common/SignatureDecoder.sol"; import "./common/SecuredTokenTransfer.sol"; import "./interfaces/ISignatureValidator.sol"; import "./external/GnosisSafeMath.sol"; -/// @title Gnosis Safe - A multisignature wallet with support for confirmations using signed messages based on ERC191. -/// @author Stefan George - -/// @author Richard Meissner - -/// @author Ricardo Guilherme Schmidt - (Status Research & Development GmbH) - Gas Token Payment contract GnosisSafe - is MasterCopy, ModuleManager, OwnerManager, SignatureDecoder, SecuredTokenTransfer, ISignatureValidatorConstants, FallbackManager { + is ModuleManager, OwnerManager, SignatureDecoder, SecuredTokenTransfer, ISignatureValidatorConstants { using GnosisSafeMath for uint256; - string public constant NAME = "Gnosis Safe"; - string public constant VERSION = "1.2.0"; - - //keccak256( - // "EIP712Domain(address verifyingContract)" - //); - bytes32 private constant DOMAIN_SEPARATOR_TYPEHASH = 0x035aff83d86937d35b32e04f0ddc6ff469290eef2f1b692d8a815c89404d4749; - - //keccak256( - // "SafeTx(address to,uint256 value,bytes data,uint8 operation,uint256 safeTxGas,uint256 baseGas,uint256 gasPrice,address gasToken,address refundReceiver,uint256 nonce)" - //); bytes32 private constant SAFE_TX_TYPEHASH = 0xbb8310d486368db6bd6f849402fdd73ad53d316b5a4b2644ad6efe0f941286d8; - //keccak256( - // "SafeMessage(bytes message)" - //); bytes32 private constant SAFE_MSG_TYPEHASH = 0x60b3cbf8b4a223d68d641b3b6ddf9a298e7f33710cf3d3a9d1146b5a6150fbca; - event ApproveHash( - bytes32 indexed approvedHash, - address indexed owner - ); - event SignMsg( - bytes32 indexed msgHash - ); event ExecutionFailure( bytes32 txHash, uint256 payment ); @@ -1705,66 +449,9 @@ contract GnosisSafe uint256 public nonce; bytes32 public domainSeparator; - // Mapping to keep track of all message hashes that have been approve by ALL REQUIRED owners mapping(bytes32 => uint256) public signedMessages; - // Mapping to keep track of all hashes (message or transaction) that have been approve by ANY owners mapping(address => mapping(bytes32 => uint256)) public approvedHashes; - // This constructor ensures that this contract can only be used as a master copy for Proxy contracts - constructor() { - // By setting the threshold it is not possible to call setup anymore, - // so we create a Safe with 0 owners and threshold 1. - // This is an unusable Safe, perfect for the mastercopy - threshold = 1; - } - - /// @dev Setup function sets initial storage of contract. - /// @param _owners List of Safe owners. - /// @param _threshold Number of required confirmations for a Safe transaction. - /// @param to Contract address for optional delegate call. - /// @param data Data payload for optional delegate call. - /// @param fallbackHandler Handler for fallback calls to this contract - /// @param paymentToken Token that should be used for the payment (0 is ETH) - /// @param payment Value that should be paid - /// @param paymentReceiver Address that should receive the payment (or 0 if tx.origin) - function setup( - address[] calldata _owners, - uint256 _threshold, - address to, - bytes calldata data, - address fallbackHandler, - address paymentToken, - uint256 payment, - address payable paymentReceiver - ) - external - { - require(domainSeparator == 0, "Domain Separator already set!"); - domainSeparator = keccak256(abi.encode(DOMAIN_SEPARATOR_TYPEHASH, this)); - setupOwners(_owners, _threshold); - if (fallbackHandler != address(0)) internalSetFallbackHandler(fallbackHandler); - // As setupOwners can only be called if the contract has not been initialized we don't need a check for setupModules - setupModules(to, data); - - if (payment > 0) { - // To avoid running into issues with EIP-170 we reuse the handlePayment function (to avoid adjusting code of that has been verified we do not adjust the method itself) - // baseGas = 0, gasPrice = 1 and gas = payment => amount = (payment + 0) * 1 = payment - handlePayment(payment, 0, 1, paymentToken, paymentReceiver); - } - } - - /// @dev Allows to execute a Safe transaction confirmed by required number of owners and then pays the account that submitted the transaction. - /// Note: The fees are always transferred, even if the user transaction fails. - /// @param to Destination address of Safe transaction. - /// @param value Ether value of Safe transaction. - /// @param data Data payload of Safe transaction. - /// @param operation Operation type of Safe transaction. - /// @param safeTxGas Gas that should be used for the Safe transaction. - /// @param baseGas Gas costs for that are independent of the transaction execution(e.g. base transaction fee, signature check, payment of the refund) - /// @param gasPrice Gas price that should be used for the payment calculation. - /// @param gasToken Token address (or 0 if ETH) that is used for the payment. - /// @param refundReceiver Address of receiver of gas payment (or 0 if tx.origin). - /// @param signatures Packed signature data ({bytes32 r}{bytes32 s}{uint8 v}) function execTransaction( address to, uint256 value, @@ -1782,29 +469,21 @@ contract GnosisSafe returns (bool success) { bytes32 txHash; - // Use scope here to limit variable lifetime and prevent `stack too deep` errors { bytes memory txHashData = encodeTransactionData( to, value, data, operation, // Transaction info safeTxGas, baseGas, gasPrice, gasToken, refundReceiver, // Payment info nonce ); - // Increase nonce and execute transaction. nonce++; txHash = keccak256(txHashData); checkSignatures(txHash, txHashData, signatures, true); } - // We require some gas to emit the events (at least 2500) after the execution and some to perform code until the execution (500) - // We also include the 1/64 in the check that is not send along with a call to counteract potential shortings because of EIP-150 require(gasleft() >= (safeTxGas * 64 / 63).max(safeTxGas + 2500) + 500, "Not enough gas to execute safe transaction"); - // Use scope here to limit variable lifetime and prevent `stack too deep` errors { uint256 gasUsed = gasleft(); - // If the gasPrice is 0 we assume that nearly all available gas can be used (it is always more than safeTxGas) - // We only subtract 2500 (compared to the 3000 before) to ensure that the amount passed is still higher than safeTxGas success = execute(to, value, data, operation, gasPrice == 0 ? (gasleft() - 2500) : safeTxGas); gasUsed = gasUsed.sub(gasleft()); - // We transfer the calculated tx costs to the tx.origin to avoid sending it to intermediate contracts that have made calls uint256 payment = 0; if (gasPrice > 0) { payment = handlePayment(gasUsed, baseGas, gasPrice, gasToken, refundReceiver); @@ -1824,12 +503,9 @@ contract GnosisSafe private returns (uint256 payment) { - // solium-disable-next-line security/no-tx-origin address payable receiver = refundReceiver == address(0) ? payable(tx.origin) : refundReceiver; if (gasToken == address(0)) { - // For ETH we will only adjust the gas price to not be higher than the actual used gas price payment = gasUsed.add(baseGas).mul(gasPrice < tx.gasprice ? gasPrice : tx.gasprice); - // solium-disable-next-line security/no-send require(receiver.send(payment), "Could not pay gas costs with ether"); } else { payment = gasUsed.add(baseGas).mul(gasPrice); @@ -1837,23 +513,12 @@ contract GnosisSafe } } - /** - * @dev Checks whether the signature provided is valid for the provided data, hash. Will revert otherwise. - * @param dataHash Hash of the data (could be either a message hash or transaction hash) - * @param data That should be signed (this is passed to an external validator contract) - * @param signatures Signature data that should be verified. Can be ECDSA signature, contract signature (EIP-1271) or approved hash. - * @param consumeHash Indicates that in case of an approved hash the storage can be freed to save gas - */ function checkSignatures(bytes32 dataHash, bytes memory data, bytes memory signatures, bool consumeHash) internal { - // Load threshold to avoid multiple storage loads uint256 _threshold = threshold; - // Check that a threshold is set require(_threshold > 0, "Threshold needs to be defined!"); - // Check that the provided signature data is not too short require(signatures.length >= _threshold.mul(65), "Signatures data too short"); - // There cannot be an owner with address 0. address lastOwner = address(0); address currentOwner; uint8 v; @@ -1862,50 +527,31 @@ contract GnosisSafe uint256 i; for (i = 0; i < _threshold; i++) { (v, r, s) = signatureSplit(signatures, i); - // If v is 0 then it is a contract signature if (v == 0) { - // When handling contract signatures the address of the contract is encoded into r currentOwner = address(uint160(uint256(r))); - // Check that signature data pointer (s) is not pointing inside the static part of the signatures bytes - // This check is not completely accurate, since it is possible that more signatures than the threshold are send. - // Here we only check that the pointer is not pointing inside the part that is being processed require(uint256(s) >= _threshold.mul(65), "Invalid contract signature location: inside static part"); - - // Check that signature data pointer (s) is in bounds (points to the length of data -> 32 bytes) require(uint256(s).add(32) <= signatures.length, "Invalid contract signature location: length not present"); - // Check if the contract signature is in bounds: start of data is s + 32 and end is start + signature length uint256 contractSignatureLen; - // solium-disable-next-line security/no-inline-assembly assembly { contractSignatureLen := mload(add(add(signatures, s), 0x20)) } require(uint256(s).add(32).add(contractSignatureLen) <= signatures.length, "Invalid contract signature location: data not complete"); - // Check signature bytes memory contractSignature; - // solium-disable-next-line security/no-inline-assembly assembly { - // The signature data for contract signatures is appended to the concatenated signatures and the offset is stored in s contractSignature := add(add(signatures, s), 0x20) } - require(ISignatureValidator(currentOwner).isValidSignature(data, contractSignature) == EIP1271_MAGIC_VALUE, "Invalid contract signature provided"); - // If v is 1 then it is an approved hash } else if (v == 1) { - // When handling approved hashes the address of the approver is encoded into r currentOwner = address(uint160(uint256(r))); - // Hashes are automatically approved by the sender of the message or when they have been pre-approved via a separate transaction require(msg.sender == currentOwner || approvedHashes[currentOwner][dataHash] != 0, "Hash has not been approved"); - // Hash has been marked for consumption. If this hash was pre-approved free storage if (consumeHash && msg.sender != currentOwner) { approvedHashes[currentOwner][dataHash] = 0; } } else if (v > 30) { - // To support eth_sign and similar we adjust v and hash the messageHash with the Ethereum message prefix before applying ecrecover currentOwner = ecrecover(keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", dataHash)), v - 4, r, s); } else { - // Use ecrecover with the messageHash for EOA signatures currentOwner = ecrecover(dataHash, v, r, s); } require ( @@ -1916,66 +562,6 @@ contract GnosisSafe } } - /// @dev Allows to estimate a Safe transaction. - /// This method is only meant for estimation purpose, therefore two different protection mechanism against execution in a transaction have been made: - /// 1.) The method can only be called from the safe itself - /// 2.) The response is returned with a revert - /// When estimating set `from` to the address of the safe. - /// Since the `estimateGas` function includes refunds, call this method to get an estimated of the costs that are deducted from the safe with `execTransaction` - /// @param to Destination address of Safe transaction. - /// @param value Ether value of Safe transaction. - /// @param data Data payload of Safe transaction. - /// @param operation Operation type of Safe transaction. - /// @return Estimate without refunds and overhead fees (base transaction and payload data gas costs). - function requiredTxGas(address to, uint256 value, bytes calldata data, Enum.Operation operation) - external - authorized - returns (uint256) - { - uint256 startGas = gasleft(); - // We don't provide an error message here, as we use it to return the estimate - // solium-disable-next-line error-reason - require(execute(to, value, data, operation, gasleft())); - uint256 requiredGas = startGas - gasleft(); - // Convert response to string and return via error message - revert(string(abi.encodePacked(requiredGas))); - } - - /** - * @dev Marks a hash as approved. This can be used to validate a hash that is used by a signature. - * @param hashToApprove The hash that should be marked as approved for signatures that are verified by this contract. - */ - function approveHash(bytes32 hashToApprove) - external - { - require(owners[msg.sender] != address(0), "Only owners can approve a hash"); - approvedHashes[msg.sender][hashToApprove] = 1; - emit ApproveHash(hashToApprove, msg.sender); - } - - /** - * @dev Marks a message as signed, so that it can be used with EIP-1271 - * @notice Marks a message (`_data`) as signed. - * @param _data Arbitrary length data that should be marked as signed on the behalf of address(this) - */ - function signMessage(bytes calldata _data) - external - authorized - { - bytes32 msgHash = getMessageHash(_data); - signedMessages[msgHash] = 1; - emit SignMsg(msgHash); - } - - /** - * Implementation of ISignatureValidator (see `interfaces/ISignatureValidator.sol`) - * @dev Should return whether the signature provided is valid for the provided data. - * The save does not implement the interface since `checkSignatures` is not a view method. - * The method will not perform any state changes (see parameters of `checkSignatures`) - * @param _data Arbitrary length data signed on the behalf of address(this) - * @param _signature Signature byte array associated with _data - * @return a bool upon valid or invalid signature with corresponding _data - */ function isValidSignature(bytes calldata _data, bytes calldata _signature) external returns (bytes4) @@ -1984,15 +570,11 @@ contract GnosisSafe if (_signature.length == 0) { require(signedMessages[messageHash] != 0, "Hash not approved"); } else { - // consumeHash needs to be false, as the state should not be changed checkSignatures(messageHash, _data, _signature, false); } return EIP1271_MAGIC_VALUE; } - /// @dev Returns hash of a message that can be signed by owners. - /// @param message Message that should be hashed - /// @return Message hash. function getMessageHash( bytes memory message ) @@ -2008,18 +590,6 @@ contract GnosisSafe ); } - /// @dev Returns the bytes that are hashed to be signed by owners. - /// @param to Destination address. - /// @param value Ether value. - /// @param data Data payload. - /// @param operation Operation type. - /// @param safeTxGas Fas that should be used for the safe transaction. - /// @param baseGas Gas costs for data used to trigger the safe transaction. - /// @param gasPrice Maximum gas price that should be used for this transaction. - /// @param gasToken Token address (or 0 if ETH) that is used for the payment. - /// @param refundReceiver Address of receiver of gas payment (or 0 if tx.origin). - /// @param _nonce Transaction nonce. - /// @return Transaction hash bytes. function encodeTransactionData( address to, uint256 value, @@ -2041,948 +611,4 @@ contract GnosisSafe ); return abi.encodePacked(bytes1(0x19), bytes1(0x01), domainSeparator, safeTxHash); } - - /// @dev Returns hash to be signed by owners. - /// @param to Destination address. - /// @param value Ether value. - /// @param data Data payload. - /// @param operation Operation type. - /// @param safeTxGas Fas that should be used for the safe transaction. - /// @param baseGas Gas costs for data used to trigger the safe transaction. - /// @param gasPrice Maximum gas price that should be used for this transaction. - /// @param gasToken Token address (or 0 if ETH) that is used for the payment. - /// @param refundReceiver Address of receiver of gas payment (or 0 if tx.origin). - /// @param _nonce Transaction nonce. - /// @return Transaction hash. - function getTransactionHash( - 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 (bytes32) - { - return keccak256(encodeTransactionData(to, value, data, operation, safeTxGas, baseGas, gasPrice, gasToken, refundReceiver, _nonce)); - } } -==== Source: ./contracts/handler/DefaultCallbackHandler.sol ==== -pragma experimental SMTChecker; -pragma solidity >=0.5.0; - -import "../interfaces/ERC1155TokenReceiver.sol"; -import "../interfaces/ERC721TokenReceiver.sol"; -import "../interfaces/ERC777TokensRecipient.sol"; - -/// @title Default Callback Handler - returns true for known token callbacks -/// @author Richard Meissner - -contract DefaultCallbackHandler is ERC1155TokenReceiver, ERC777TokensRecipient, ERC721TokenReceiver { - - string public constant NAME = "Default Callback Handler"; - string public constant VERSION = "1.0.0"; - - function onERC1155Received(address, address, uint256, uint256, bytes calldata) override - external - returns(bytes4) - { - return 0xf23a6e61; - } - - function onERC1155BatchReceived(address, address, uint256[] calldata, uint256[] calldata, bytes calldata) override - external - returns(bytes4) - { - return 0xbc197c81; - } - - function onERC721Received(address, address, uint256, bytes calldata) override - external - returns(bytes4) - { - return 0x150b7a02; - } - - // solium-disable-next-line no-empty-blocks - function tokensReceived(address, address, address, uint256, bytes calldata, bytes calldata) external override { - // We implement this for completeness, doesn't really have any value - } - -} -==== Source: mock-contract/contracts/MockContract.sol ==== -pragma experimental SMTChecker; -// SPDX-License-Identifier: UNLICENSED - -pragma solidity >=0.7.0 <0.9.0; - -interface MockInterface { - /** - * @dev After calling this method, the mock will return `response` when it is called - * with any calldata that is not mocked more specifically below - * (e.g. using givenMethodReturn). - * @param response ABI encoded response that will be returned if method is invoked - */ - function givenAnyReturn(bytes calldata response) external; - function givenAnyReturnBool(bool response) external; - function givenAnyReturnUint(uint response) external; - function givenAnyReturnAddress(address response) external; - - function givenAnyRevert() external; - function givenAnyRevertWithMessage(string calldata message) external; - function givenAnyRunOutOfGas() external; - - /** - * @dev After calling this method, the mock will return `response` when the given - * methodId is called regardless of arguments. If the methodId and arguments - * are mocked more specifically (using `givenMethodAndArguments`) the latter - * will take precedence. - * @param method ABI encoded methodId. It is valid to pass full calldata (including arguments). The mock will extract the methodId from it - * @param response ABI encoded response that will be returned if method is invoked - */ - function givenMethodReturn(bytes calldata method, bytes calldata response) external; - function givenMethodReturnBool(bytes calldata method, bool response) external; - function givenMethodReturnUint(bytes calldata method, uint response) external; - function givenMethodReturnAddress(bytes calldata method, address response) external; - - function givenMethodRevert(bytes calldata method) external; - function givenMethodRevertWithMessage(bytes calldata method, string calldata message) external; - function givenMethodRunOutOfGas(bytes calldata method) external; - - /** - * @dev After calling this method, the mock will return `response` when the given - * methodId is called with matching arguments. These exact calldataMocks will take - * precedence over all other calldataMocks. - * @param call ABI encoded calldata (methodId and arguments) - * @param response ABI encoded response that will be returned if contract is invoked with calldata - */ - function givenCalldataReturn(bytes calldata call, bytes calldata response) external; - function givenCalldataReturnBool(bytes calldata call, bool response) external; - function givenCalldataReturnUint(bytes calldata call, uint response) external; - function givenCalldataReturnAddress(bytes calldata call, address response) external; - - function givenCalldataRevert(bytes calldata call) external; - function givenCalldataRevertWithMessage(bytes calldata call, string calldata message) external; - function givenCalldataRunOutOfGas(bytes calldata call) external; - - /** - * @dev Returns the number of times anything has been called on this mock since last reset - */ - function invocationCount() external returns (uint); - - /** - * @dev Returns the number of times the given method has been called on this mock since last reset - * @param method ABI encoded methodId. It is valid to pass full calldata (including arguments). The mock will extract the methodId from it - */ - function invocationCountForMethod(bytes calldata method) external returns (uint); - - /** - * @dev Returns the number of times this mock has been called with the exact calldata since last reset. - * @param call ABI encoded calldata (methodId and arguments) - */ - function invocationCountForCalldata(bytes calldata call) external returns (uint); - - /** - * @dev Resets all mocked methods and invocation counts. - */ - function reset() external; -} - -/** - * Implementation of the MockInterface. - */ -contract MockContract is MockInterface { - enum MockType { Return, Revert, OutOfGas } - - bytes32 public constant MOCKS_LIST_START = hex"01"; - bytes public constant MOCKS_LIST_END = "0xff"; - bytes32 public constant MOCKS_LIST_END_HASH = keccak256(MOCKS_LIST_END); - bytes4 public constant SENTINEL_ANY_MOCKS = hex"01"; - bytes public constant DEFAULT_FALLBACK_VALUE = abi.encode(false); - - // A linked list allows easy iteration and inclusion checks - mapping(bytes32 => bytes) calldataMocks; - mapping(bytes => MockType) calldataMockTypes; - mapping(bytes => bytes) calldataExpectations; - mapping(bytes => string) calldataRevertMessage; - mapping(bytes32 => uint) calldataInvocations; - - mapping(bytes4 => bytes4) methodIdMocks; - mapping(bytes4 => MockType) methodIdMockTypes; - mapping(bytes4 => bytes) methodIdExpectations; - mapping(bytes4 => string) methodIdRevertMessages; - mapping(bytes32 => uint) methodIdInvocations; - - MockType fallbackMockType; - bytes fallbackExpectation = DEFAULT_FALLBACK_VALUE; - string fallbackRevertMessage; - uint invocations; - uint resetCount; - - constructor() { - calldataMocks[MOCKS_LIST_START] = MOCKS_LIST_END; - methodIdMocks[SENTINEL_ANY_MOCKS] = SENTINEL_ANY_MOCKS; - } - - function trackCalldataMock(bytes memory call) private { - bytes32 callHash = keccak256(call); - if (calldataMocks[callHash].length == 0) { - calldataMocks[callHash] = calldataMocks[MOCKS_LIST_START]; - calldataMocks[MOCKS_LIST_START] = call; - } - } - - function trackMethodIdMock(bytes4 methodId) private { - if (methodIdMocks[methodId] == 0x0) { - methodIdMocks[methodId] = methodIdMocks[SENTINEL_ANY_MOCKS]; - methodIdMocks[SENTINEL_ANY_MOCKS] = methodId; - } - } - - function _givenAnyReturn(bytes memory response) internal { - fallbackMockType = MockType.Return; - fallbackExpectation = response; - } - - function givenAnyReturn(bytes calldata response) override external { - _givenAnyReturn(response); - } - - function givenAnyReturnBool(bool response) override external { - uint flag = response ? 1 : 0; - _givenAnyReturn(uintToBytes(flag)); - } - - function givenAnyReturnUint(uint response) override external { - _givenAnyReturn(uintToBytes(response)); - } - - function givenAnyReturnAddress(address response) override external { - _givenAnyReturn(uintToBytes(uint(uint160(response)))); - } - - function givenAnyRevert() override external { - fallbackMockType = MockType.Revert; - fallbackRevertMessage = ""; - } - - function givenAnyRevertWithMessage(string calldata message) override external { - fallbackMockType = MockType.Revert; - fallbackRevertMessage = message; - } - - function givenAnyRunOutOfGas() override external { - fallbackMockType = MockType.OutOfGas; - } - - function _givenCalldataReturn(bytes memory call, bytes memory response) private { - calldataMockTypes[call] = MockType.Return; - calldataExpectations[call] = response; - trackCalldataMock(call); - } - - function givenCalldataReturn(bytes calldata call, bytes calldata response) override external { - _givenCalldataReturn(call, response); - } - - function givenCalldataReturnBool(bytes calldata call, bool response) override external { - uint flag = response ? 1 : 0; - _givenCalldataReturn(call, uintToBytes(flag)); - } - - function givenCalldataReturnUint(bytes calldata call, uint response) override external { - _givenCalldataReturn(call, uintToBytes(response)); - } - - function givenCalldataReturnAddress(bytes calldata call, address response) override external { - _givenCalldataReturn(call, uintToBytes(uint(uint160(response)))); - } - - function _givenMethodReturn(bytes memory call, bytes memory response) private { - bytes4 method = bytesToBytes4(call); - methodIdMockTypes[method] = MockType.Return; - methodIdExpectations[method] = response; - trackMethodIdMock(method); - } - - function givenMethodReturn(bytes calldata call, bytes calldata response) override external { - _givenMethodReturn(call, response); - } - - function givenMethodReturnBool(bytes calldata call, bool response) override external { - uint flag = response ? 1 : 0; - _givenMethodReturn(call, uintToBytes(flag)); - } - - function givenMethodReturnUint(bytes calldata call, uint response) override external { - _givenMethodReturn(call, uintToBytes(response)); - } - - function givenMethodReturnAddress(bytes calldata call, address response) override external { - _givenMethodReturn(call, uintToBytes(uint(uint160(response)))); - } - - function givenCalldataRevert(bytes calldata call) override external { - calldataMockTypes[call] = MockType.Revert; - calldataRevertMessage[call] = ""; - trackCalldataMock(call); - } - - function givenMethodRevert(bytes calldata call) override external { - bytes4 method = bytesToBytes4(call); - methodIdMockTypes[method] = MockType.Revert; - trackMethodIdMock(method); - } - - function givenCalldataRevertWithMessage(bytes calldata call, string calldata message) override external { - calldataMockTypes[call] = MockType.Revert; - calldataRevertMessage[call] = message; - trackCalldataMock(call); - } - - function givenMethodRevertWithMessage(bytes calldata call, string calldata message) override external { - bytes4 method = bytesToBytes4(call); - methodIdMockTypes[method] = MockType.Revert; - methodIdRevertMessages[method] = message; - trackMethodIdMock(method); - } - - function givenCalldataRunOutOfGas(bytes calldata call) override external { - calldataMockTypes[call] = MockType.OutOfGas; - trackCalldataMock(call); - } - - function givenMethodRunOutOfGas(bytes calldata call) override external { - bytes4 method = bytesToBytes4(call); - methodIdMockTypes[method] = MockType.OutOfGas; - trackMethodIdMock(method); - } - - function invocationCount() override external view returns (uint) { - return invocations; - } - - function invocationCountForMethod(bytes calldata call) override external view returns (uint) { - bytes4 method = bytesToBytes4(call); - return methodIdInvocations[keccak256(abi.encodePacked(resetCount, method))]; - } - - function invocationCountForCalldata(bytes calldata call) override external view returns (uint) { - return calldataInvocations[keccak256(abi.encodePacked(resetCount, call))]; - } - - function reset() override external { - // Reset all exact calldataMocks - bytes memory nextMock = calldataMocks[MOCKS_LIST_START]; - bytes32 mockHash = keccak256(nextMock); - // We cannot compary bytes - while(mockHash != MOCKS_LIST_END_HASH) { - // Reset all mock maps - calldataMockTypes[nextMock] = MockType.Return; - calldataExpectations[nextMock] = hex""; - calldataRevertMessage[nextMock] = ""; - // Set next mock to remove - nextMock = calldataMocks[mockHash]; - // Remove from linked list - calldataMocks[mockHash] = ""; - // Update mock hash - mockHash = keccak256(nextMock); - } - // Clear list - calldataMocks[MOCKS_LIST_START] = MOCKS_LIST_END; - - // Reset all any calldataMocks - bytes4 nextAnyMock = methodIdMocks[SENTINEL_ANY_MOCKS]; - while(nextAnyMock != SENTINEL_ANY_MOCKS) { - bytes4 currentAnyMock = nextAnyMock; - methodIdMockTypes[currentAnyMock] = MockType.Return; - methodIdExpectations[currentAnyMock] = hex""; - methodIdRevertMessages[currentAnyMock] = ""; - nextAnyMock = methodIdMocks[currentAnyMock]; - // Remove from linked list - methodIdMocks[currentAnyMock] = 0x0; - } - // Clear list - methodIdMocks[SENTINEL_ANY_MOCKS] = SENTINEL_ANY_MOCKS; - - fallbackExpectation = DEFAULT_FALLBACK_VALUE; - fallbackMockType = MockType.Return; - invocations = 0; - resetCount += 1; - } - - function useAllGas() private { - while(true) { - bool s; - assembly { - //expensive call to EC multiply contract - s := call(sub(gas(), 2000), 6, 0, 0x0, 0xc0, 0x0, 0x60) - } - } - } - - function bytesToBytes4(bytes memory b) private pure returns (bytes4) { - bytes4 out; - for (uint i = 0; i < 4; i++) { - out |= bytes4(b[i] & 0xFF) >> (i * 8); - } - return out; - } - - function uintToBytes(uint256 x) private pure returns (bytes memory b) { - b = new bytes(32); - assembly { mstore(add(b, 32), x) } - } - - function updateInvocationCount(bytes4 methodId, bytes memory originalMsgData) public { - require(msg.sender == address(this), "Can only be called from the contract itself"); - invocations += 1; - methodIdInvocations[keccak256(abi.encodePacked(resetCount, methodId))] += 1; - calldataInvocations[keccak256(abi.encodePacked(resetCount, originalMsgData))] += 1; - } - - receive() payable external { - fallbackImpl(); - } - fallback() payable external { - fallbackImpl(); - } - - function fallbackImpl() internal { - bytes4 methodId = msg.sig; - - // First, check exact matching overrides - if (calldataMockTypes[msg.data] == MockType.Revert) { - revert(calldataRevertMessage[msg.data]); - } - if (calldataMockTypes[msg.data] == MockType.OutOfGas) { - useAllGas(); - } - bytes memory result = calldataExpectations[msg.data]; - - // Then check method Id overrides - if (result.length == 0) { - if (methodIdMockTypes[methodId] == MockType.Revert) { - revert(methodIdRevertMessages[methodId]); - } - if (methodIdMockTypes[methodId] == MockType.OutOfGas) { - useAllGas(); - } - result = methodIdExpectations[methodId]; - } - - // Last, use the fallback override - if (result.length == 0) { - if (fallbackMockType == MockType.Revert) { - revert(fallbackRevertMessage); - } - if (fallbackMockType == MockType.OutOfGas) { - useAllGas(); - } - result = fallbackExpectation; - } - - // Record invocation as separate call so we don't rollback in case we are called with STATICCALL - (, bytes memory r) = address(this).call{gas: 100000}(abi.encodeWithSignature("updateInvocationCount(bytes4,bytes)", methodId, msg.data)); - assert(r.length == 0); - - assembly { - return(add(0x20, result), mload(result)) - } - } -} - -==== Source: mock-contract/contracts/ExampleContractUnderTest.sol ==== -pragma experimental SMTChecker; -// SPDX-License-Identifier: UNLICENSED - -pragma solidity >0.7.0 <0.9.0; - -import './ComplexInterface.sol'; - -contract ExampleContractUnderTest { - ComplexInterface complexInterface; - - constructor(address _complexInterface) { - complexInterface = ComplexInterface(_complexInterface); - } - - function callMockedFunction3Times() public view returns (bool) { - complexInterface.acceptUintReturnUintView(1); - complexInterface.acceptUintReturnUintView(1); - complexInterface.acceptUintReturnUintView(1); - return true; - } - - function callMethodThatReturnsAddress() public returns (address) { - address foo = complexInterface.acceptUintReturnAddress(1); - return foo; - } - - function callMethodThatReturnsBool() public returns (bool) { - return complexInterface.acceptUintReturnBool(1); - } -} - -==== Source: mock-contract/contracts/ComplexInterface.sol ==== -pragma experimental SMTChecker; -// SPDX-License-Identifier: UNLICENSED - -pragma solidity >=0.7.0 <0.9.0; - -/** - * @dev Used for unit testing MockContract functionality. - */ -interface ComplexInterface { - function methodA() external; - function methodB() external; - function acceptAdressUintReturnBool(address recipient, uint amount) external returns (bool); - function acceptUintReturnString(uint) external returns (string memory); - function acceptUintReturnBool(uint) external returns (bool); - function acceptUintReturnUint(uint) external returns (uint); - function acceptUintReturnAddress(uint) external returns (address); - function acceptUintReturnUintView(uint) external view returns (uint); -} - -==== Source: mock-contract/flattened.sol ==== -pragma experimental SMTChecker; -==== Source: ./contracts/MockContract.sol ==== -pragma experimental SMTChecker; -// SPDX-License-Identifier: UNLICENSED - -pragma solidity >=0.7.0 <0.9.0; - -interface MockInterface { - /** - * @dev After calling this method, the mock will return `response` when it is called - * with any calldata that is not mocked more specifically below - * (e.g. using givenMethodReturn). - * @param response ABI encoded response that will be returned if method is invoked - */ - function givenAnyReturn(bytes calldata response) external; - function givenAnyReturnBool(bool response) external; - function givenAnyReturnUint(uint response) external; - function givenAnyReturnAddress(address response) external; - - function givenAnyRevert() external; - function givenAnyRevertWithMessage(string calldata message) external; - function givenAnyRunOutOfGas() external; - - /** - * @dev After calling this method, the mock will return `response` when the given - * methodId is called regardless of arguments. If the methodId and arguments - * are mocked more specifically (using `givenMethodAndArguments`) the latter - * will take precedence. - * @param method ABI encoded methodId. It is valid to pass full calldata (including arguments). The mock will extract the methodId from it - * @param response ABI encoded response that will be returned if method is invoked - */ - function givenMethodReturn(bytes calldata method, bytes calldata response) external; - function givenMethodReturnBool(bytes calldata method, bool response) external; - function givenMethodReturnUint(bytes calldata method, uint response) external; - function givenMethodReturnAddress(bytes calldata method, address response) external; - - function givenMethodRevert(bytes calldata method) external; - function givenMethodRevertWithMessage(bytes calldata method, string calldata message) external; - function givenMethodRunOutOfGas(bytes calldata method) external; - - /** - * @dev After calling this method, the mock will return `response` when the given - * methodId is called with matching arguments. These exact calldataMocks will take - * precedence over all other calldataMocks. - * @param call ABI encoded calldata (methodId and arguments) - * @param response ABI encoded response that will be returned if contract is invoked with calldata - */ - function givenCalldataReturn(bytes calldata call, bytes calldata response) external; - function givenCalldataReturnBool(bytes calldata call, bool response) external; - function givenCalldataReturnUint(bytes calldata call, uint response) external; - function givenCalldataReturnAddress(bytes calldata call, address response) external; - - function givenCalldataRevert(bytes calldata call) external; - function givenCalldataRevertWithMessage(bytes calldata call, string calldata message) external; - function givenCalldataRunOutOfGas(bytes calldata call) external; - - /** - * @dev Returns the number of times anything has been called on this mock since last reset - */ - function invocationCount() external returns (uint); - - /** - * @dev Returns the number of times the given method has been called on this mock since last reset - * @param method ABI encoded methodId. It is valid to pass full calldata (including arguments). The mock will extract the methodId from it - */ - function invocationCountForMethod(bytes calldata method) external returns (uint); - - /** - * @dev Returns the number of times this mock has been called with the exact calldata since last reset. - * @param call ABI encoded calldata (methodId and arguments) - */ - function invocationCountForCalldata(bytes calldata call) external returns (uint); - - /** - * @dev Resets all mocked methods and invocation counts. - */ - function reset() external; -} - -/** - * Implementation of the MockInterface. - */ -contract MockContract is MockInterface { - enum MockType { Return, Revert, OutOfGas } - - bytes32 public constant MOCKS_LIST_START = hex"01"; - bytes public constant MOCKS_LIST_END = "0xff"; - bytes32 public constant MOCKS_LIST_END_HASH = keccak256(MOCKS_LIST_END); - bytes4 public constant SENTINEL_ANY_MOCKS = hex"01"; - bytes public constant DEFAULT_FALLBACK_VALUE = abi.encode(false); - - // A linked list allows easy iteration and inclusion checks - mapping(bytes32 => bytes) calldataMocks; - mapping(bytes => MockType) calldataMockTypes; - mapping(bytes => bytes) calldataExpectations; - mapping(bytes => string) calldataRevertMessage; - mapping(bytes32 => uint) calldataInvocations; - - mapping(bytes4 => bytes4) methodIdMocks; - mapping(bytes4 => MockType) methodIdMockTypes; - mapping(bytes4 => bytes) methodIdExpectations; - mapping(bytes4 => string) methodIdRevertMessages; - mapping(bytes32 => uint) methodIdInvocations; - - MockType fallbackMockType; - bytes fallbackExpectation = DEFAULT_FALLBACK_VALUE; - string fallbackRevertMessage; - uint invocations; - uint resetCount; - - constructor() { - calldataMocks[MOCKS_LIST_START] = MOCKS_LIST_END; - methodIdMocks[SENTINEL_ANY_MOCKS] = SENTINEL_ANY_MOCKS; - } - - function trackCalldataMock(bytes memory call) private { - bytes32 callHash = keccak256(call); - if (calldataMocks[callHash].length == 0) { - calldataMocks[callHash] = calldataMocks[MOCKS_LIST_START]; - calldataMocks[MOCKS_LIST_START] = call; - } - } - - function trackMethodIdMock(bytes4 methodId) private { - if (methodIdMocks[methodId] == 0x0) { - methodIdMocks[methodId] = methodIdMocks[SENTINEL_ANY_MOCKS]; - methodIdMocks[SENTINEL_ANY_MOCKS] = methodId; - } - } - - function _givenAnyReturn(bytes memory response) internal { - fallbackMockType = MockType.Return; - fallbackExpectation = response; - } - - function givenAnyReturn(bytes calldata response) override external { - _givenAnyReturn(response); - } - - function givenAnyReturnBool(bool response) override external { - uint flag = response ? 1 : 0; - _givenAnyReturn(uintToBytes(flag)); - } - - function givenAnyReturnUint(uint response) override external { - _givenAnyReturn(uintToBytes(response)); - } - - function givenAnyReturnAddress(address response) override external { - _givenAnyReturn(uintToBytes(uint(uint160(response)))); - } - - function givenAnyRevert() override external { - fallbackMockType = MockType.Revert; - fallbackRevertMessage = ""; - } - - function givenAnyRevertWithMessage(string calldata message) override external { - fallbackMockType = MockType.Revert; - fallbackRevertMessage = message; - } - - function givenAnyRunOutOfGas() override external { - fallbackMockType = MockType.OutOfGas; - } - - function _givenCalldataReturn(bytes memory call, bytes memory response) private { - calldataMockTypes[call] = MockType.Return; - calldataExpectations[call] = response; - trackCalldataMock(call); - } - - function givenCalldataReturn(bytes calldata call, bytes calldata response) override external { - _givenCalldataReturn(call, response); - } - - function givenCalldataReturnBool(bytes calldata call, bool response) override external { - uint flag = response ? 1 : 0; - _givenCalldataReturn(call, uintToBytes(flag)); - } - - function givenCalldataReturnUint(bytes calldata call, uint response) override external { - _givenCalldataReturn(call, uintToBytes(response)); - } - - function givenCalldataReturnAddress(bytes calldata call, address response) override external { - _givenCalldataReturn(call, uintToBytes(uint(uint160(response)))); - } - - function _givenMethodReturn(bytes memory call, bytes memory response) private { - bytes4 method = bytesToBytes4(call); - methodIdMockTypes[method] = MockType.Return; - methodIdExpectations[method] = response; - trackMethodIdMock(method); - } - - function givenMethodReturn(bytes calldata call, bytes calldata response) override external { - _givenMethodReturn(call, response); - } - - function givenMethodReturnBool(bytes calldata call, bool response) override external { - uint flag = response ? 1 : 0; - _givenMethodReturn(call, uintToBytes(flag)); - } - - function givenMethodReturnUint(bytes calldata call, uint response) override external { - _givenMethodReturn(call, uintToBytes(response)); - } - - function givenMethodReturnAddress(bytes calldata call, address response) override external { - _givenMethodReturn(call, uintToBytes(uint(uint160(response)))); - } - - function givenCalldataRevert(bytes calldata call) override external { - calldataMockTypes[call] = MockType.Revert; - calldataRevertMessage[call] = ""; - trackCalldataMock(call); - } - - function givenMethodRevert(bytes calldata call) override external { - bytes4 method = bytesToBytes4(call); - methodIdMockTypes[method] = MockType.Revert; - trackMethodIdMock(method); - } - - function givenCalldataRevertWithMessage(bytes calldata call, string calldata message) override external { - calldataMockTypes[call] = MockType.Revert; - calldataRevertMessage[call] = message; - trackCalldataMock(call); - } - - function givenMethodRevertWithMessage(bytes calldata call, string calldata message) override external { - bytes4 method = bytesToBytes4(call); - methodIdMockTypes[method] = MockType.Revert; - methodIdRevertMessages[method] = message; - trackMethodIdMock(method); - } - - function givenCalldataRunOutOfGas(bytes calldata call) override external { - calldataMockTypes[call] = MockType.OutOfGas; - trackCalldataMock(call); - } - - function givenMethodRunOutOfGas(bytes calldata call) override external { - bytes4 method = bytesToBytes4(call); - methodIdMockTypes[method] = MockType.OutOfGas; - trackMethodIdMock(method); - } - - function invocationCount() override external view returns (uint) { - return invocations; - } - - function invocationCountForMethod(bytes calldata call) override external view returns (uint) { - bytes4 method = bytesToBytes4(call); - return methodIdInvocations[keccak256(abi.encodePacked(resetCount, method))]; - } - - function invocationCountForCalldata(bytes calldata call) override external view returns (uint) { - return calldataInvocations[keccak256(abi.encodePacked(resetCount, call))]; - } - - function reset() override external { - // Reset all exact calldataMocks - bytes memory nextMock = calldataMocks[MOCKS_LIST_START]; - bytes32 mockHash = keccak256(nextMock); - // We cannot compary bytes - while(mockHash != MOCKS_LIST_END_HASH) { - // Reset all mock maps - calldataMockTypes[nextMock] = MockType.Return; - calldataExpectations[nextMock] = hex""; - calldataRevertMessage[nextMock] = ""; - // Set next mock to remove - nextMock = calldataMocks[mockHash]; - // Remove from linked list - calldataMocks[mockHash] = ""; - // Update mock hash - mockHash = keccak256(nextMock); - } - // Clear list - calldataMocks[MOCKS_LIST_START] = MOCKS_LIST_END; - - // Reset all any calldataMocks - bytes4 nextAnyMock = methodIdMocks[SENTINEL_ANY_MOCKS]; - while(nextAnyMock != SENTINEL_ANY_MOCKS) { - bytes4 currentAnyMock = nextAnyMock; - methodIdMockTypes[currentAnyMock] = MockType.Return; - methodIdExpectations[currentAnyMock] = hex""; - methodIdRevertMessages[currentAnyMock] = ""; - nextAnyMock = methodIdMocks[currentAnyMock]; - // Remove from linked list - methodIdMocks[currentAnyMock] = 0x0; - } - // Clear list - methodIdMocks[SENTINEL_ANY_MOCKS] = SENTINEL_ANY_MOCKS; - - fallbackExpectation = DEFAULT_FALLBACK_VALUE; - fallbackMockType = MockType.Return; - invocations = 0; - resetCount += 1; - } - - function useAllGas() private { - while(true) { - bool s; - assembly { - //expensive call to EC multiply contract - s := call(sub(gas(), 2000), 6, 0, 0x0, 0xc0, 0x0, 0x60) - } - } - } - - function bytesToBytes4(bytes memory b) private pure returns (bytes4) { - bytes4 out; - for (uint i = 0; i < 4; i++) { - out |= bytes4(b[i] & 0xFF) >> (i * 8); - } - return out; - } - - function uintToBytes(uint256 x) private pure returns (bytes memory b) { - b = new bytes(32); - assembly { mstore(add(b, 32), x) } - } - - function updateInvocationCount(bytes4 methodId, bytes memory originalMsgData) public { - require(msg.sender == address(this), "Can only be called from the contract itself"); - invocations += 1; - methodIdInvocations[keccak256(abi.encodePacked(resetCount, methodId))] += 1; - calldataInvocations[keccak256(abi.encodePacked(resetCount, originalMsgData))] += 1; - } - - receive() payable external { - fallbackImpl(); - } - fallback() payable external { - fallbackImpl(); - } - - function fallbackImpl() internal { - bytes4 methodId = msg.sig; - - // First, check exact matching overrides - if (calldataMockTypes[msg.data] == MockType.Revert) { - revert(calldataRevertMessage[msg.data]); - } - if (calldataMockTypes[msg.data] == MockType.OutOfGas) { - useAllGas(); - } - bytes memory result = calldataExpectations[msg.data]; - - // Then check method Id overrides - if (result.length == 0) { - if (methodIdMockTypes[methodId] == MockType.Revert) { - revert(methodIdRevertMessages[methodId]); - } - if (methodIdMockTypes[methodId] == MockType.OutOfGas) { - useAllGas(); - } - result = methodIdExpectations[methodId]; - } - - // Last, use the fallback override - if (result.length == 0) { - if (fallbackMockType == MockType.Revert) { - revert(fallbackRevertMessage); - } - if (fallbackMockType == MockType.OutOfGas) { - useAllGas(); - } - result = fallbackExpectation; - } - - // Record invocation as separate call so we don't rollback in case we are called with STATICCALL - (, bytes memory r) = address(this).call{gas: 100000}(abi.encodeWithSignature("updateInvocationCount(bytes4,bytes)", methodId, msg.data)); - assert(r.length == 0); - - assembly { - return(add(0x20, result), mload(result)) - } - } -} - -==== Source: ./contracts/ExampleContractUnderTest.sol ==== -pragma experimental SMTChecker; -// SPDX-License-Identifier: UNLICENSED - -pragma solidity >0.7.0 <0.9.0; - -import './ComplexInterface.sol'; - -contract ExampleContractUnderTest { - ComplexInterface complexInterface; - - constructor(address _complexInterface) { - complexInterface = ComplexInterface(_complexInterface); - } - - function callMockedFunction3Times() public view returns (bool) { - complexInterface.acceptUintReturnUintView(1); - complexInterface.acceptUintReturnUintView(1); - complexInterface.acceptUintReturnUintView(1); - return true; - } - - function callMethodThatReturnsAddress() public returns (address) { - address foo = complexInterface.acceptUintReturnAddress(1); - return foo; - } - - function callMethodThatReturnsBool() public returns (bool) { - return complexInterface.acceptUintReturnBool(1); - } -} - -==== Source: ./contracts/ComplexInterface.sol ==== -pragma experimental SMTChecker; -// SPDX-License-Identifier: UNLICENSED - -pragma solidity >=0.7.0 <0.9.0; - -/** - * @dev Used for unit testing MockContract functionality. - */ -interface ComplexInterface { - function methodA() external; - function methodB() external; - function acceptAdressUintReturnBool(address recipient, uint amount) external returns (bool); - function acceptUintReturnString(uint) external returns (string memory); - function acceptUintReturnBool(uint) external returns (bool); - function acceptUintReturnUint(uint) external returns (uint); - function acceptUintReturnAddress(uint) external returns (address); - function acceptUintReturnUintView(uint) external view returns (uint); -} - -