==== 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 smalles 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 transfered (0 for Ether) /// @param to Address to which the tokens should be transfered /// @param amount Amount of tokens (or Ether) that should be transfered 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 "./ModuleManager.sol"; /// @title Module - Base class for modules. /// @author Stefan George - /// @author Richard Meissner - contract Module is MasterCopy { ModuleManager public manager; modifier authorized() override { require(msg.sender == address(manager), "Method can only be called from manager"); _; } function setManager() internal { // manager can only be 0 at initalization 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; pragma solidity >=0.5.0; import "../common/Enum.sol"; import "../common/SelfAuthorized.sol"; 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); event DisabledModule(Module module); event ExecutionFromModuleSuccess(address indexed module); event ExecutionFromModuleFailure(address indexed module); address internal constant SENTINEL_MODULES = address(0x1); mapping (address => address) internal modules; function 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)]; modules[address(module)] = address(0); 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 returns (bool) { 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) { array[moduleCount] = currentModule; currentModule = modules[currentModule]; moduleCount++; } next = currentModule; // Set correct size of returned array // solium-disable-next-line security/no-inline-assembly assembly { mstore(array, moduleCount) } } } ==== Source: ./contracts/base/OwnerManager.sol ==== 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); event RemovedOwner(address owner); event ChangedThreshold(uint256 threshold); address internal constant SENTINEL_OWNERS = address(0x1); mapping(address => address) internal owners; uint256 ownerCount; uint256 internal threshold; /// @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; } owners[currentOwner] = SENTINEL_OWNERS; ownerCount = _owners.length; 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]; owners[prevOwner] = newOwner; owners[oldOwner] = address(0); emit RemovedOwner(oldOwner); 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); } function getThreshold() public view returns (uint256) { return threshold; } function isOwner(address owner) public view returns (bool) { return owner != SENTINEL_OWNERS && owners[owner] != address(0); } /// @dev Returns array of owners. /// @return Array of Safe owners. function getOwners() public view returns (address[] memory) { address[] memory array = new address[](ownerCount); // populate return array uint256 index = 0; address currentOwner = owners[SENTINEL_OWNERS]; while(currentOwner != SENTINEL_OWNERS) { array[index] = currentOwner; currentOwner = owners[currentOwner]; index ++; } return array; } } ==== Source: ./contracts/base/Executor.sol ==== pragma experimental SMTChecker; pragma solidity >=0.5.0; import "../common/Enum.sol"; /// @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) internal returns (bool success) { if (operation == Enum.Operation.Call) success = executeCall(to, value, data, txGas); else if (operation == Enum.Operation.DelegateCall) success = executeDelegateCall(to, data, txGas); else success = false; } function executeCall(address to, uint256 value, bytes memory data, uint256 txGas) internal returns (bool success) { // solium-disable-next-line security/no-inline-assembly assembly { success := call(txGas, to, value, add(data, 0x20), mload(data), 0, 0) } } function executeDelegateCall(address to, bytes memory data, uint256 txGas) internal returns (bool success) { // 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) public view 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 concatinated. (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, uint256 amount ) internal 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) mstore(0x40, add(ptr, returndatasize())) returndatacopy(ptr, 0, returndatasize()) switch returndatasize() case 0 { transferred := success } case 0x20 { transferred := iszero(or(iszero(success), iszero(mload(ptr)))) } default { transferred := 0 } } } } ==== Source: ./contracts/common/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"); _; } } ==== Source: ./contracts/common/SignatureDecoder.sol ==== 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, uint256 pos ) internal pure returns (address) { uint8 v; bytes32 r; bytes32 s; (v, r, s) = signatureSplit(messageSignature, pos); return ecrecover(messageHash, v, r, s); } /// @dev divides bytes signature into `uint8 v, bytes32 r, bytes32 s`. /// @notice Make sure to peform 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) } } } ==== Source: ./contracts/common/Enum.sol ==== 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; } uint256 c = a * b; require(c / a == b); 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; 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); 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 { 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 ); event ExecutionSuccess( bytes32 txHash, uint256 payment ); 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 Adddress 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 transfered, 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 indipendent 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, bytes calldata data, Enum.Operation operation, uint256 safeTxGas, uint256 baseGas, uint256 gasPrice, address gasToken, address payable refundReceiver, bytes calldata signatures ) external payable returns (bool success) { bytes32 txHash; // 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 substract 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); } if (success) emit ExecutionSuccess(txHash, payment); else emit ExecutionFailure(txHash, payment); } } function handlePayment( uint256 gasUsed, uint256 baseGas, uint256 gasPrice, address gasToken, address payable refundReceiver ) private returns (uint256 payment) { // 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); require(transferToken(gasToken, receiver, payment), "Could not pay gas costs with token"); } } /** * @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; bytes32 r; bytes32 s; 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 ( currentOwner > lastOwner && owners[currentOwner] != address(0) && currentOwner != SENTINEL_OWNERS, "Invalid owner provided" ); lastOwner = currentOwner; } } /// @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) { bytes32 messageHash = getMessageHash(_data); 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 ) public view returns (bytes32) { bytes32 safeMessageHash = keccak256( abi.encode(SAFE_MSG_TYPEHASH, keccak256(message)) ); return keccak256( abi.encodePacked(bytes1(0x19), bytes1(0x01), domainSeparator, safeMessageHash) ); } /// @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, bytes memory data, Enum.Operation operation, uint256 safeTxGas, uint256 baseGas, uint256 gasPrice, address gasToken, address refundReceiver, uint256 _nonce ) public view returns (bytes memory) { bytes32 safeTxHash = keccak256( abi.encode(SAFE_TX_TYPEHASH, to, value, keccak256(data), operation, safeTxGas, baseGas, gasPrice, gasToken, refundReceiver, _nonce) ); return abi.encodePacked(bytes1(0x19), bytes1(0x01), domainSeparator, safeTxHash); } /// @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); }