mirror of
https://github.com/luxdao/zodiac.git
synced 2026-10-30 20:34:38 +00:00
116 lines
130 KiB
JSON
116 lines
130 KiB
JSON
{
|
|
"contractName": "Roles",
|
|
"sourceName": "contracts/Roles.sol",
|
|
"compilerVersion": "v0.8.21+commit.d9974bed",
|
|
"constructorArguments": "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001",
|
|
"input": {
|
|
"language": "Solidity",
|
|
"sources": {
|
|
"@gnosis.pm/safe-contracts/contracts/common/Enum.sol": {
|
|
"content": "// SPDX-License-Identifier: LGPL-3.0-only\npragma solidity >=0.7.0 <0.9.0;\n\n/// @title Enum - Collection of enums\n/// @author Richard Meissner - <[email protected]>\ncontract Enum {\n enum Operation {Call, DelegateCall}\n}\n"
|
|
},
|
|
"@gnosis.pm/zodiac/contracts/core/Modifier.sol": {
|
|
"content": "// SPDX-License-Identifier: LGPL-3.0-only\npragma solidity >=0.7.0 <0.9.0;\n\nimport {Enum} from \"@gnosis.pm/safe-contracts/contracts/common/Enum.sol\";\nimport {ExecutionTracker} from \"../signature/ExecutionTracker.sol\";\nimport {IAvatar} from \"../interfaces/IAvatar.sol\";\nimport {Module} from \"./Module.sol\";\nimport {SignatureChecker} from \"../signature/SignatureChecker.sol\";\n\n/// @title Modifier Interface - A contract that sits between a Module and an Avatar and enforce some additional logic.\nabstract contract Modifier is\n Module,\n ExecutionTracker,\n SignatureChecker,\n IAvatar\n{\n address internal constant SENTINEL_MODULES = address(0x1);\n /// Mapping of modules.\n mapping(address => address) internal modules;\n\n /// `sender` is not an authorized module.\n /// @param sender The address of the sender.\n error NotAuthorized(address sender);\n\n /// `module` is invalid.\n error InvalidModule(address module);\n\n /// `pageSize` is invalid.\n error InvalidPageSize();\n\n /// `module` is already disabled.\n error AlreadyDisabledModule(address module);\n\n /// `module` is already enabled.\n error AlreadyEnabledModule(address module);\n\n /// @dev `setModules()` was already called.\n error SetupModulesAlreadyCalled();\n\n /*\n --------------------------------------------------\n You must override both of the following virtual functions,\n execTransactionFromModule() and execTransactionFromModuleReturnData().\n It is recommended that implementations of both functions make use the \n onlyModule modifier.\n */\n\n /// @dev Passes a transaction to the modifier.\n /// @notice Can only be called by enabled modules.\n /// @param to Destination address of module transaction.\n /// @param value Ether value of module transaction.\n /// @param data Data payload of module transaction.\n /// @param operation Operation type of module transaction.\n function execTransactionFromModule(\n address to,\n uint256 value,\n bytes calldata data,\n Enum.Operation operation\n ) public virtual returns (bool success);\n\n /// @dev Passes a transaction to the modifier, expects return data.\n /// @notice Can only be called by enabled modules.\n /// @param to Destination address of module transaction.\n /// @param value Ether value of module transaction.\n /// @param data Data payload of module transaction.\n /// @param operation Operation type of module transaction.\n function execTransactionFromModuleReturnData(\n address to,\n uint256 value,\n bytes calldata data,\n Enum.Operation operation\n ) public virtual returns (bool success, bytes memory returnData);\n\n /*\n --------------------------------------------------\n */\n\n modifier moduleOnly() {\n if (modules[msg.sender] == address(0)) {\n (bytes32 hash, address signer) = moduleTxSignedBy();\n\n // is the signer a module?\n if (modules[signer] == address(0)) {\n revert NotAuthorized(msg.sender);\n }\n\n // is the provided signature fresh?\n if (consumed[signer][hash]) {\n revert HashAlreadyConsumed(hash);\n }\n\n consumed[signer][hash] = true;\n emit HashExecuted(hash);\n }\n\n _;\n }\n\n function sentOrSignedByModule() internal view returns (address) {\n if (modules[msg.sender] != address(0)) {\n return msg.sender;\n }\n\n (, address signer) = moduleTxSignedBy();\n if (modules[signer] != address(0)) {\n return signer;\n }\n\n return address(0);\n }\n\n /// @dev Disables a module on the modifier.\n /// @notice This can only be called by the owner.\n /// @param prevModule Module that pointed to the module to be removed in the linked list.\n /// @param module Module to be removed.\n function disableModule(\n address prevModule,\n address module\n ) public override onlyOwner {\n if (module == address(0) || module == SENTINEL_MODULES)\n revert InvalidModule(module);\n if (modules[prevModule] != module) revert AlreadyDisabledModule(module);\n modules[prevModule] = modules[module];\n modules[module] = address(0);\n emit DisabledModule(module);\n }\n\n /// @dev Enables a module that can add transactions to the queue\n /// @param module Address of the module to be enabled\n /// @notice This can only be called by the owner\n function enableModule(address module) public override onlyOwner {\n if (module == address(0) || module == SENTINEL_MODULES)\n revert InvalidModule(module);\n if (modules[module] != address(0)) revert AlreadyEnabledModule(module);\n modules[module] = modules[SENTINEL_MODULES];\n modules[SENTINEL_MODULES] = module;\n emit EnabledModule(module);\n }\n\n /// @dev Returns if an module is enabled\n /// @return True if the module is enabled\n function isModuleEnabled(\n address _module\n ) public view override returns (bool) {\n return SENTINEL_MODULES != _module && modules[_module] != address(0);\n }\n\n /// @dev Returns array of modules.\n /// If all entries fit into a single page, the next pointer will be 0x1.\n /// If another page is present, next will be the last element of the returned array.\n /// @param start Start of the page. Has to be a module or start pointer (0x1 address)\n /// @param pageSize Maximum number of modules that should be returned. Has to be > 0\n /// @return array Array of modules.\n /// @return next Start of the next page.\n function getModulesPaginated(\n address start,\n uint256 pageSize\n ) external view override returns (address[] memory array, address next) {\n if (start != SENTINEL_MODULES && !isModuleEnabled(start)) {\n revert InvalidModule(start);\n }\n if (pageSize == 0) {\n revert InvalidPageSize();\n }\n\n // Init array with max page size\n array = new address[](pageSize);\n\n // Populate return array\n uint256 moduleCount = 0;\n next = modules[start];\n while (\n next != address(0) && next != SENTINEL_MODULES && moduleCount < pageSize\n ) {\n array[moduleCount] = next;\n next = modules[next];\n moduleCount++;\n }\n\n // Because of the argument validation we can assume that\n // the `currentModule` will always be either a module address\n // or sentinel address (aka the end). If we haven't reached the end\n // inside the loop, we need to set the next pointer to the last element\n // because it skipped over to the next module which is neither included\n // in the current page nor won't be included in the next one\n // if you pass it as a start.\n if (next != SENTINEL_MODULES) {\n next = array[moduleCount - 1];\n }\n // Set correct size of returned array\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(array, moduleCount)\n }\n }\n\n /// @dev Initializes the modules linked list.\n /// @notice Should be called as part of the `setUp` / initializing function and can only be called once.\n function setupModules() internal {\n if (modules[SENTINEL_MODULES] != address(0))\n revert SetupModulesAlreadyCalled();\n modules[SENTINEL_MODULES] = SENTINEL_MODULES;\n }\n}\n"
|
|
},
|
|
"@gnosis.pm/zodiac/contracts/core/Module.sol": {
|
|
"content": "// SPDX-License-Identifier: LGPL-3.0-only\npragma solidity >=0.7.0 <0.9.0;\n\nimport {Enum} from \"@gnosis.pm/safe-contracts/contracts/common/Enum.sol\";\n\nimport {FactoryFriendly} from \"../factory/FactoryFriendly.sol\";\nimport {IAvatar} from \"../interfaces/IAvatar.sol\";\n\n/// @title Module Interface - A contract that can pass messages to a Module Manager contract if enabled by that contract.\nabstract contract Module is FactoryFriendly {\n /// @dev Address that will ultimately execute function calls.\n address public avatar;\n /// @dev Address that this module will pass transactions to.\n address public target;\n\n /// @dev Emitted each time the avatar is set.\n event AvatarSet(address indexed previousAvatar, address indexed newAvatar);\n /// @dev Emitted each time the Target is set.\n event TargetSet(address indexed previousTarget, address indexed newTarget);\n\n /// @dev Sets the avatar to a new avatar (`newAvatar`).\n /// @notice Can only be called by the current owner.\n function setAvatar(address _avatar) public onlyOwner {\n address previousAvatar = avatar;\n avatar = _avatar;\n emit AvatarSet(previousAvatar, _avatar);\n }\n\n /// @dev Sets the target to a new target (`newTarget`).\n /// @notice Can only be called by the current owner.\n function setTarget(address _target) public onlyOwner {\n address previousTarget = target;\n target = _target;\n emit TargetSet(previousTarget, _target);\n }\n\n /// @dev Passes a transaction to be executed by the avatar.\n /// @notice Can only be called by this contract.\n /// @param to Destination address of module transaction.\n /// @param value Ether value of module transaction.\n /// @param data Data payload of module transaction.\n /// @param operation Operation type of module transaction: 0 == call, 1 == delegate call.\n function exec(\n address to,\n uint256 value,\n bytes memory data,\n Enum.Operation operation\n ) internal virtual returns (bool success) {\n return\n IAvatar(target).execTransactionFromModule(to, value, data, operation);\n }\n\n /// @dev Passes a transaction to be executed by the target and returns data.\n /// @notice Can only be called by this contract.\n /// @param to Destination address of module transaction.\n /// @param value Ether value of module transaction.\n /// @param data Data payload of module transaction.\n /// @param operation Operation type of module transaction: 0 == call, 1 == delegate call.\n function execAndReturnData(\n address to,\n uint256 value,\n bytes memory data,\n Enum.Operation operation\n ) internal virtual returns (bool success, bytes memory returnData) {\n return\n IAvatar(target).execTransactionFromModuleReturnData(\n to,\n value,\n data,\n operation\n );\n }\n}\n"
|
|
},
|
|
"@gnosis.pm/zodiac/contracts/factory/FactoryFriendly.sol": {
|
|
"content": "// SPDX-License-Identifier: LGPL-3.0-only\n\n/// @title Zodiac FactoryFriendly - A contract that allows other contracts to be initializable and pass bytes as arguments to define contract state\npragma solidity >=0.7.0 <0.9.0;\n\nimport {OwnableUpgradeable} from \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\n\nabstract contract FactoryFriendly is OwnableUpgradeable {\n function setUp(bytes memory initializeParams) public virtual;\n}\n"
|
|
},
|
|
"@gnosis.pm/zodiac/contracts/interfaces/IAvatar.sol": {
|
|
"content": "// SPDX-License-Identifier: LGPL-3.0-only\n\n/// @title Zodiac Avatar - A contract that manages modules that can execute transactions via this contract.\npragma solidity >=0.7.0 <0.9.0;\n\nimport {Enum} from \"@gnosis.pm/safe-contracts/contracts/common/Enum.sol\";\n\ninterface IAvatar {\n event EnabledModule(address module);\n event DisabledModule(address module);\n event ExecutionFromModuleSuccess(address indexed module);\n event ExecutionFromModuleFailure(address indexed module);\n\n /// @dev Enables a module on the avatar.\n /// @notice Can only be called by the avatar.\n /// @notice Modules should be stored as a linked list.\n /// @notice Must emit EnabledModule(address module) if successful.\n /// @param module Module to be enabled.\n function enableModule(address module) external;\n\n /// @dev Disables a module on the avatar.\n /// @notice Can only be called by the avatar.\n /// @notice Must emit DisabledModule(address module) if successful.\n /// @param prevModule Address that pointed to the module to be removed in the linked list\n /// @param module Module to be removed.\n function disableModule(address prevModule, address module) external;\n\n /// @dev Allows a Module to execute a transaction.\n /// @notice Can only be called by an enabled module.\n /// @notice Must emit ExecutionFromModuleSuccess(address module) if successful.\n /// @notice Must emit ExecutionFromModuleFailure(address module) if unsuccessful.\n /// @param to Destination address of module transaction.\n /// @param value Ether value of module transaction.\n /// @param data Data payload of module transaction.\n /// @param operation Operation type of module transaction: 0 == call, 1 == delegate call.\n function execTransactionFromModule(\n address to,\n uint256 value,\n bytes memory data,\n Enum.Operation operation\n ) external returns (bool success);\n\n /// @dev Allows a Module to execute a transaction and return data\n /// @notice Can only be called by an enabled module.\n /// @notice Must emit ExecutionFromModuleSuccess(address module) if successful.\n /// @notice Must emit ExecutionFromModuleFailure(address module) if unsuccessful.\n /// @param to Destination address of module transaction.\n /// @param value Ether value of module transaction.\n /// @param data Data payload of module transaction.\n /// @param operation Operation type of module transaction: 0 == call, 1 == delegate call.\n function execTransactionFromModuleReturnData(\n address to,\n uint256 value,\n bytes memory data,\n Enum.Operation operation\n ) external returns (bool success, bytes memory returnData);\n\n /// @dev Returns if an module is enabled\n /// @return True if the module is enabled\n function isModuleEnabled(address module) external view returns (bool);\n\n /// @dev Returns array of modules.\n /// @param start Start of the page.\n /// @param pageSize Maximum number of modules that should be returned.\n /// @return array Array of modules.\n /// @return next Start of the next page.\n function getModulesPaginated(\n address start,\n uint256 pageSize\n ) external view returns (address[] memory array, address next);\n}\n"
|
|
},
|
|
"@gnosis.pm/zodiac/contracts/signature/ExecutionTracker.sol": {
|
|
"content": "// SPDX-License-Identifier: LGPL-3.0-only\npragma solidity >=0.8.0 <0.9.0;\n\n/// @title ExecutionTracker - A contract that keeps track of executed and invalidated hashes\ncontract ExecutionTracker {\n error HashAlreadyConsumed(bytes32);\n\n event HashExecuted(bytes32);\n event HashInvalidated(bytes32);\n\n mapping(address => mapping(bytes32 => bool)) public consumed;\n\n function invalidate(bytes32 hash) external {\n consumed[msg.sender][hash] = true;\n emit HashInvalidated(hash);\n }\n}\n"
|
|
},
|
|
"@gnosis.pm/zodiac/contracts/signature/IERC1271.sol": {
|
|
"content": "// SPDX-License-Identifier: LGPL-3.0-only\n/* solhint-disable one-contract-per-file */\npragma solidity >=0.7.0 <0.9.0;\n\ninterface IERC1271 {\n /**\n * @notice EIP1271 method to validate a signature.\n * @param hash Hash of the data signed on the behalf of address(this).\n * @param signature Signature byte array associated with _data.\n *\n * MUST return the bytes4 magic value 0x1626ba7e when function passes.\n * MUST NOT modify state (using STATICCALL for solc < 0.5, view modifier for solc > 0.5)\n * MUST allow external calls\n */\n function isValidSignature(\n bytes32 hash,\n bytes memory signature\n ) external view returns (bytes4);\n}\n"
|
|
},
|
|
"@gnosis.pm/zodiac/contracts/signature/SignatureChecker.sol": {
|
|
"content": "// SPDX-License-Identifier: LGPL-3.0-only\npragma solidity >=0.8.0 <0.9.0;\n\nimport {IERC1271} from \"./IERC1271.sol\";\n\n/// @title SignatureChecker - A contract that retrieves and validates signatures appended to transaction calldata.\n/// @dev currently supports eip-712 and eip-1271 signatures\nabstract contract SignatureChecker {\n /**\n * @notice Searches for a signature, validates it, and returns the signer's address.\n * @dev When signature not found or invalid, zero address is returned\n * @return The address of the signer.\n */\n function moduleTxSignedBy() internal view returns (bytes32, address) {\n bytes calldata data = msg.data;\n\n /*\n * The idea is to extend `onlyModule` and provide signature checking\n * without code changes to inheriting contracts (Modifiers).\n *\n * Since it's a generic mechanism, there is no way to conclusively\n * identify the trailing bytes as a signature. We simply slice those\n * and recover signer.\n *\n * As a result, we impose a minimum calldata length equal to a function\n * selector plus salt, plus a signature (i.e., 4 + 32 + 65 bytes), any\n * shorter and calldata it guaranteed to not contain a signature.\n */\n if (data.length < 4 + 32 + 65) {\n return (bytes32(0), address(0));\n }\n\n (uint8 v, bytes32 r, bytes32 s) = _splitSignature(data);\n\n uint256 end = data.length - (32 + 65);\n bytes32 salt = bytes32(data[end:]);\n\n /*\n * When handling contract signatures:\n * v - is zero\n * r - contains the signer\n * s - contains the offset within calldata where the signer specific\n * signature is located\n *\n * We detect contract signatures by checking:\n * 1- `v` is zero\n * 2- `s` points within the buffer, is after selector, is before\n * salt and delimits a non-zero length buffer\n */\n if (v == 0) {\n uint256 start = uint256(s);\n if (start < 4 || start > end) {\n return (bytes32(0), address(0));\n }\n address signer = address(uint160(uint256(r)));\n\n bytes32 hash = moduleTxHash(data[:start], salt);\n return\n _isValidContractSignature(signer, hash, data[start:end])\n ? (hash, signer)\n : (bytes32(0), address(0));\n } else {\n bytes32 hash = moduleTxHash(data[:end], salt);\n return (hash, ecrecover(hash, v, r, s));\n }\n }\n\n /**\n * @notice Hashes the transaction EIP-712 data structure.\n * @dev The produced hash is intended to be signed.\n * @param data The current transaction's calldata.\n * @param salt The salt value.\n * @return The 32-byte hash that is to be signed.\n */\n function moduleTxHash(\n bytes calldata data,\n bytes32 salt\n ) public view returns (bytes32) {\n bytes32 domainSeparator = keccak256(\n abi.encode(DOMAIN_SEPARATOR_TYPEHASH, block.chainid, this)\n );\n bytes memory moduleTxData = abi.encodePacked(\n bytes1(0x19),\n bytes1(0x01),\n domainSeparator,\n keccak256(abi.encode(MODULE_TX_TYPEHASH, keccak256(data), salt))\n );\n return keccak256(moduleTxData);\n }\n\n /**\n * @dev Extracts signature from calldata, and divides it into `uint8 v, bytes32 r, bytes32 s`.\n * @param data The current transaction's calldata.\n * @return v The ECDSA v value\n * @return r The ECDSA r value\n * @return s The ECDSA s value\n */\n function _splitSignature(\n bytes calldata data\n ) private pure returns (uint8 v, bytes32 r, bytes32 s) {\n v = uint8(bytes1(data[data.length - 1:]));\n r = bytes32(data[data.length - 65:]);\n s = bytes32(data[data.length - 33:]);\n }\n\n /**\n * @dev Calls the signer contract, and validates the contract signature.\n * @param signer The address of the signer contract.\n * @param hash Hash of the data signed\n * @param signature The contract signature.\n * @return result Indicates whether the signature is valid.\n */\n function _isValidContractSignature(\n address signer,\n bytes32 hash,\n bytes calldata signature\n ) internal view returns (bool result) {\n uint256 size;\n // eslint-disable-line no-inline-assembly\n assembly {\n size := extcodesize(signer)\n }\n if (size == 0) {\n return false;\n }\n\n (bool success, bytes memory returnData) = signer.staticcall(\n abi.encodeWithSelector(\n IERC1271.isValidSignature.selector,\n hash,\n signature\n )\n );\n\n return success && bytes4(returnData) == EIP1271_MAGIC_VALUE;\n }\n\n // keccak256(\n // \"EIP712Domain(uint256 chainId,address verifyingContract)\"\n // );\n bytes32 private constant DOMAIN_SEPARATOR_TYPEHASH =\n 0x47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a79469218;\n\n // keccak256(\n // \"ModuleTx(bytes data,bytes32 salt)\"\n // );\n bytes32 private constant MODULE_TX_TYPEHASH =\n 0x2939aeeda3ca260200c9f7b436b19e13207547ccc65cfedc857751c5ea6d91d4;\n\n // bytes4(keccak256(\n // \"isValidSignature(bytes32,bytes)\"\n // ));\n bytes4 private constant EIP1271_MAGIC_VALUE = 0x1626ba7e;\n}\n"
|
|
},
|
|
"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol": {
|
|
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)\n\npragma solidity ^0.8.20;\n\nimport {ContextUpgradeable} from \"../utils/ContextUpgradeable.sol\";\nimport {Initializable} from \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * The initial owner is set to the address provided by the deployer. This can\n * later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {\n /// @custom:storage-location erc7201:openzeppelin.storage.Ownable\n struct OwnableStorage {\n address _owner;\n }\n\n // keccak256(abi.encode(uint256(keccak256(\"openzeppelin.storage.Ownable\")) - 1)) & ~bytes32(uint256(0xff))\n bytes32 private constant OwnableStorageLocation = 0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300;\n\n function _getOwnableStorage() private pure returns (OwnableStorage storage $) {\n assembly {\n $.slot := OwnableStorageLocation\n }\n }\n\n /**\n * @dev The caller account is not authorized to perform an operation.\n */\n error OwnableUnauthorizedAccount(address account);\n\n /**\n * @dev The owner is not a valid owner account. (eg. `address(0)`)\n */\n error OwnableInvalidOwner(address owner);\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the address provided by the deployer as the initial owner.\n */\n function __Ownable_init(address initialOwner) internal onlyInitializing {\n __Ownable_init_unchained(initialOwner);\n }\n\n function __Ownable_init_unchained(address initialOwner) internal onlyInitializing {\n if (initialOwner == address(0)) {\n revert OwnableInvalidOwner(address(0));\n }\n _transferOwnership(initialOwner);\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n _checkOwner();\n _;\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n OwnableStorage storage $ = _getOwnableStorage();\n return $._owner;\n }\n\n /**\n * @dev Throws if the sender is not the owner.\n */\n function _checkOwner() internal view virtual {\n if (owner() != _msgSender()) {\n revert OwnableUnauthorizedAccount(_msgSender());\n }\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby disabling any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n if (newOwner == address(0)) {\n revert OwnableInvalidOwner(address(0));\n }\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n OwnableStorage storage $ = _getOwnableStorage();\n address oldOwner = $._owner;\n $._owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n"
|
|
},
|
|
"@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol": {
|
|
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed\n * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an\n * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer\n * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.\n *\n * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be\n * reused. This mechanism prevents re-execution of each \"step\" but allows the creation of new initialization steps in\n * case an upgrade adds a module that needs to be initialized.\n *\n * For example:\n *\n * [.hljs-theme-light.nopadding]\n * ```solidity\n * contract MyToken is ERC20Upgradeable {\n * function initialize() initializer public {\n * __ERC20_init(\"MyToken\", \"MTK\");\n * }\n * }\n *\n * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {\n * function initializeV2() reinitializer(2) public {\n * __ERC20Permit_init(\"MyToken\");\n * }\n * }\n * ```\n *\n * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as\n * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.\n *\n * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure\n * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.\n *\n * [CAUTION]\n * ====\n * Avoid leaving a contract uninitialized.\n *\n * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation\n * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke\n * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:\n *\n * [.hljs-theme-light.nopadding]\n * ```\n * /// @custom:oz-upgrades-unsafe-allow constructor\n * constructor() {\n * _disableInitializers();\n * }\n * ```\n * ====\n */\nabstract contract Initializable {\n /**\n * @dev Storage of the initializable contract.\n *\n * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions\n * when using with upgradeable contracts.\n *\n * @custom:storage-location erc7201:openzeppelin.storage.Initializable\n */\n struct InitializableStorage {\n /**\n * @dev Indicates that the contract has been initialized.\n */\n uint64 _initialized;\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool _initializing;\n }\n\n // keccak256(abi.encode(uint256(keccak256(\"openzeppelin.storage.Initializable\")) - 1)) & ~bytes32(uint256(0xff))\n bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;\n\n /**\n * @dev The contract is already initialized.\n */\n error InvalidInitialization();\n\n /**\n * @dev The contract is not initializing.\n */\n error NotInitializing();\n\n /**\n * @dev Triggered when the contract has been initialized or reinitialized.\n */\n event Initialized(uint64 version);\n\n /**\n * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,\n * `onlyInitializing` functions can be used to initialize parent contracts.\n *\n * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any\n * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in\n * production.\n *\n * Emits an {Initialized} event.\n */\n modifier initializer() {\n // solhint-disable-next-line var-name-mixedcase\n InitializableStorage storage $ = _getInitializableStorage();\n\n // Cache values to avoid duplicated sloads\n bool isTopLevelCall = !$._initializing;\n uint64 initialized = $._initialized;\n\n // Allowed calls:\n // - initialSetup: the contract is not in the initializing state and no previous version was\n // initialized\n // - construction: the contract is initialized at version 1 (no reininitialization) and the\n // current contract is just being deployed\n bool initialSetup = initialized == 0 && isTopLevelCall;\n bool construction = initialized == 1 && address(this).code.length == 0;\n\n if (!initialSetup && !construction) {\n revert InvalidInitialization();\n }\n $._initialized = 1;\n if (isTopLevelCall) {\n $._initializing = true;\n }\n _;\n if (isTopLevelCall) {\n $._initializing = false;\n emit Initialized(1);\n }\n }\n\n /**\n * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the\n * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be\n * used to initialize parent contracts.\n *\n * A reinitializer may be used after the original initialization step. This is essential to configure modules that\n * are added through upgrades and that require initialization.\n *\n * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`\n * cannot be nested. If one is invoked in the context of another, execution will revert.\n *\n * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in\n * a contract, executing them in the right order is up to the developer or operator.\n *\n * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.\n *\n * Emits an {Initialized} event.\n */\n modifier reinitializer(uint64 version) {\n // solhint-disable-next-line var-name-mixedcase\n InitializableStorage storage $ = _getInitializableStorage();\n\n if ($._initializing || $._initialized >= version) {\n revert InvalidInitialization();\n }\n $._initialized = version;\n $._initializing = true;\n _;\n $._initializing = false;\n emit Initialized(version);\n }\n\n /**\n * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the\n * {initializer} and {reinitializer} modifiers, directly or indirectly.\n */\n modifier onlyInitializing() {\n _checkInitializing();\n _;\n }\n\n /**\n * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.\n */\n function _checkInitializing() internal view virtual {\n if (!_isInitializing()) {\n revert NotInitializing();\n }\n }\n\n /**\n * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.\n * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized\n * to any version. It is recommended to use this to lock implementation contracts that are designed to be called\n * through proxies.\n *\n * Emits an {Initialized} event the first time it is successfully executed.\n */\n function _disableInitializers() internal virtual {\n // solhint-disable-next-line var-name-mixedcase\n InitializableStorage storage $ = _getInitializableStorage();\n\n if ($._initializing) {\n revert InvalidInitialization();\n }\n if ($._initialized != type(uint64).max) {\n $._initialized = type(uint64).max;\n emit Initialized(type(uint64).max);\n }\n }\n\n /**\n * @dev Returns the highest version that has been initialized. See {reinitializer}.\n */\n function _getInitializedVersion() internal view returns (uint64) {\n return _getInitializableStorage()._initialized;\n }\n\n /**\n * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.\n */\n function _isInitializing() internal view returns (bool) {\n return _getInitializableStorage()._initializing;\n }\n\n /**\n * @dev Returns a pointer to the storage namespace.\n */\n // solhint-disable-next-line var-name-mixedcase\n function _getInitializableStorage() private pure returns (InitializableStorage storage $) {\n assembly {\n $.slot := INITIALIZABLE_STORAGE\n }\n }\n}\n"
|
|
},
|
|
"@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol": {
|
|
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/Context.sol)\n\npragma solidity ^0.8.20;\nimport {Initializable} from \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract ContextUpgradeable is Initializable {\n function __Context_init() internal onlyInitializing {\n }\n\n function __Context_init_unchained() internal onlyInitializing {\n }\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n"
|
|
},
|
|
"contracts/AllowanceTracker.sol": {
|
|
"content": "// SPDX-License-Identifier: LGPL-3.0-only\npragma solidity >=0.8.17 <0.9.0;\n\nimport \"./Core.sol\";\n\n/**\n * @title AllowanceTracker - a component of the Zodiac Roles Mod that is\n * responsible for loading and calculating allowance balances. Persists\n * consumptions back to storage.\n * @author Cristóvão Honorato - <[email protected]>\n * @author Jan-Felix Schwarz - <[email protected]>\n */\nabstract contract AllowanceTracker is Core {\n event ConsumeAllowance(\n bytes32 allowanceKey,\n uint128 consumed,\n uint128 newBalance\n );\n\n function _accruedAllowance(\n Allowance memory allowance,\n uint64 blockTimestamp\n ) internal pure override returns (uint128 balance, uint64 timestamp) {\n if (\n allowance.period == 0 ||\n blockTimestamp < (allowance.timestamp + allowance.period)\n ) {\n return (allowance.balance, allowance.timestamp);\n }\n\n uint64 elapsedIntervals = (blockTimestamp - allowance.timestamp) /\n allowance.period;\n\n if (allowance.balance < allowance.maxRefill) {\n balance = allowance.balance + allowance.refill * elapsedIntervals;\n\n balance = balance < allowance.maxRefill\n ? balance\n : allowance.maxRefill;\n } else {\n balance = allowance.balance;\n }\n\n timestamp = allowance.timestamp + elapsedIntervals * allowance.period;\n }\n\n /**\n * @dev Flushes the consumption of allowances back into storage, before\n * execution. This flush is not final\n * @param consumptions The array of consumption structs containing\n * information about allowances and consumed amounts.\n */\n function _flushPrepare(Consumption[] memory consumptions) internal {\n uint256 count = consumptions.length;\n\n for (uint256 i; i < count; ) {\n Consumption memory consumption = consumptions[i];\n\n bytes32 key = consumption.allowanceKey;\n uint128 consumed = consumption.consumed;\n\n // Retrieve the allowance and calculate its current updated balance\n // and next refill timestamp.\n Allowance storage allowance = allowances[key];\n (uint128 balance, uint64 timestamp) = _accruedAllowance(\n allowance,\n uint64(block.timestamp)\n );\n\n assert(balance == consumption.balance);\n assert(consumed <= balance);\n // Flush\n allowance.balance = balance - consumed;\n allowance.timestamp = timestamp;\n\n unchecked {\n ++i;\n }\n }\n }\n\n /**\n * @dev Finalizes or reverts the flush of allowances, after transaction\n * execution\n * @param consumptions The array of consumption structs containing\n * information about allowances and consumed amounts.\n * @param success a boolean that indicates whether transaction execution\n * was successful\n */\n function _flushCommit(\n Consumption[] memory consumptions,\n bool success\n ) internal {\n uint256 count = consumptions.length;\n for (uint256 i; i < count; ) {\n Consumption memory consumption = consumptions[i];\n bytes32 key = consumption.allowanceKey;\n if (success) {\n emit ConsumeAllowance(\n key,\n consumption.consumed,\n consumption.balance - consumption.consumed\n );\n } else {\n allowances[key].balance = consumption.balance;\n }\n unchecked {\n ++i;\n }\n }\n }\n}\n"
|
|
},
|
|
"contracts/Consumptions.sol": {
|
|
"content": "// SPDX-License-Identifier: LGPL-3.0-only\npragma solidity >=0.8.17 <0.9.0;\n\nimport \"./Types.sol\";\n\n/**\n * @title Consumptions - a library that provides helper functions for dealing\n * with collection of Consumptions.\n * @author Cristóvão Honorato - <[email protected]>\n */\nlibrary Consumptions {\n function clone(\n Consumption[] memory consumptions\n ) internal pure returns (Consumption[] memory result) {\n uint256 length = consumptions.length;\n\n result = new Consumption[](length);\n for (uint256 i; i < length; ) {\n result[i].allowanceKey = consumptions[i].allowanceKey;\n result[i].balance = consumptions[i].balance;\n result[i].consumed = consumptions[i].consumed;\n\n unchecked {\n ++i;\n }\n }\n }\n\n function find(\n Consumption[] memory consumptions,\n bytes32 key\n ) internal pure returns (uint256, bool) {\n uint256 length = consumptions.length;\n\n for (uint256 i; i < length; ) {\n if (consumptions[i].allowanceKey == key) {\n return (i, true);\n }\n\n unchecked {\n ++i;\n }\n }\n\n return (0, false);\n }\n\n function merge(\n Consumption[] memory c1,\n Consumption[] memory c2\n ) internal pure returns (Consumption[] memory result) {\n if (c1.length == 0) return c2;\n if (c2.length == 0) return c1;\n\n result = new Consumption[](c1.length + c2.length);\n\n uint256 length = c1.length;\n\n for (uint256 i; i < length; ) {\n result[i].allowanceKey = c1[i].allowanceKey;\n result[i].balance = c1[i].balance;\n result[i].consumed = c1[i].consumed;\n\n unchecked {\n ++i;\n }\n }\n\n for (uint256 i; i < c2.length; ) {\n (uint256 index, bool found) = find(c1, c2[i].allowanceKey);\n if (found) {\n result[index].consumed += c2[i].consumed;\n } else {\n result[length].allowanceKey = c2[i].allowanceKey;\n result[length].balance = c2[i].balance;\n result[length].consumed = c2[i].consumed;\n length++;\n }\n\n unchecked {\n ++i;\n }\n }\n\n if (length < result.length) {\n assembly {\n mstore(result, length)\n }\n }\n }\n}\n"
|
|
},
|
|
"contracts/Core.sol": {
|
|
"content": "// SPDX-License-Identifier: LGPL-3.0-only\npragma solidity >=0.8.17 <0.9.0;\n\nimport \"@gnosis.pm/zodiac/contracts/core/Modifier.sol\";\nimport \"./Types.sol\";\n\n/**\n * @title Core is the base contract for the Zodiac Roles Mod, which defines\n * the common abstract connection points between Builder, Loader, and Checker.\n * @author Cristóvão Honorato - <[email protected]>\n */\nabstract contract Core is Modifier {\n mapping(bytes32 => Role) internal roles;\n mapping(bytes32 => Allowance) public allowances;\n\n function _store(\n Role storage role,\n bytes32 key,\n ConditionFlat[] memory conditions,\n ExecutionOptions options\n ) internal virtual;\n\n function _load(\n Role storage role,\n bytes32 key\n ) internal view virtual returns (Condition memory, Consumption[] memory);\n\n function _accruedAllowance(\n Allowance memory allowance,\n uint64 blockTimestamp\n ) internal pure virtual returns (uint128 balance, uint64 timestamp);\n\n function _key(\n address targetAddress,\n bytes4 selector\n ) internal pure returns (bytes32) {\n /*\n * Unoptimized version:\n * bytes32(abi.encodePacked(targetAddress, selector))\n */\n return bytes32(bytes20(targetAddress)) | (bytes32(selector) >> 160);\n }\n}\n"
|
|
},
|
|
"contracts/Decoder.sol": {
|
|
"content": "// SPDX-License-Identifier: LGPL-3.0-only\npragma solidity >=0.8.17 <0.9.0;\n\nimport \"./Topology.sol\";\n\n/**\n * @title Decoder - a library that discovers parameter locations in calldata\n * from a list of conditions.\n * @author Cristóvão Honorato - <[email protected]>\n */\nlibrary Decoder {\n error CalldataOutOfBounds();\n\n /**\n * @dev Maps the location and size of parameters in the encoded transaction data.\n * @param data The encoded transaction data.\n * @param condition The condition of the parameters.\n * @return result The mapped location and size of parameters in the encoded transaction data.\n */\n function inspect(\n bytes calldata data,\n Condition memory condition\n ) internal pure returns (ParameterPayload memory result) {\n /*\n * In the parameter encoding area, there is a region called the head\n * that is divided into 32-byte chunks. Each parameter has its own\n * corresponding chunk in the head region:\n * - Static parameters are encoded inline.\n * - Dynamic parameters have an offset to the tail, which is the start\n * of the actual encoding for the dynamic parameter. Note that the\n * offset does not include the 4-byte function signature.\"\n *\n */\n Topology.TypeTree memory node = Topology.typeTree(condition);\n __block__(data, 4, node, node.children.length, false, result);\n result.location = 0;\n result.size = data.length;\n }\n\n /**\n * @dev Walks through a parameter encoding tree and maps their location and\n * size within calldata.\n * @param data The encoded transaction data.\n * @param location The current offset within the calldata buffer.\n * @param node The current node being traversed within the parameter tree.\n * @param result The location and size of the parameter within calldata.\n */\n function _walk(\n bytes calldata data,\n uint256 location,\n Topology.TypeTree memory node,\n ParameterPayload memory result\n ) private pure {\n ParameterType paramType = node.paramType;\n\n if (paramType == ParameterType.Static) {\n result.size = 32;\n } else if (paramType == ParameterType.Dynamic) {\n result.size = 32 + _ceil32(uint256(word(data, location)));\n } else if (paramType == ParameterType.Tuple) {\n __block__(\n data,\n location,\n node,\n node.children.length,\n false,\n result\n );\n } else if (paramType == ParameterType.Array) {\n __block__(\n data,\n location + 32,\n node,\n uint256(word(data, location)),\n true,\n result\n );\n result.size += 32;\n } else if (\n paramType == ParameterType.Calldata ||\n paramType == ParameterType.AbiEncoded\n ) {\n __block__(\n data,\n location + 32 + (paramType == ParameterType.Calldata ? 4 : 0),\n node,\n node.children.length,\n false,\n result\n );\n result.size = 32 + _ceil32(uint256(word(data, location)));\n }\n result.location = location;\n }\n\n /**\n * @dev Recursively walk through the TypeTree to decode a block of parameters.\n * @param data The encoded transaction data.\n * @param location The current location of the parameter block being processed.\n * @param node The current TypeTree node being processed.\n * @param length The number of parts in the block.\n * @param template whether first child is type descriptor for all parts.\n * @param result The decoded ParameterPayload.\n */\n function __block__(\n bytes calldata data,\n uint256 location,\n Topology.TypeTree memory node,\n uint256 length,\n bool template,\n ParameterPayload memory result\n ) private pure {\n result.children = new ParameterPayload[](length);\n bool isInline;\n if (template) isInline = Topology.isInline(node.children[0]);\n\n uint256 offset;\n for (uint256 i; i < length; ) {\n if (!template) isInline = Topology.isInline(node.children[i]);\n\n _walk(\n data,\n _locationInBlock(data, location, offset, isInline),\n node.children[template ? 0 : i],\n result.children[i]\n );\n\n uint256 childSize = result.children[i].size;\n result.size += isInline ? childSize : childSize + 32;\n offset += isInline ? childSize : 32;\n\n unchecked {\n ++i;\n }\n }\n }\n\n /**\n * @dev Returns the location of a block part, which may be located inline\n * within the block - at the HEAD - or at an offset relative to the start\n * of the block - at the TAIL.\n *\n * @param data The encoded transaction data.\n * @param location The location of the block within the calldata buffer.\n * @param offset The offset of the block part, relative to the start of the block.\n * @param isInline Whether the block part is located inline within the block.\n *\n * @return The location of the block part within the calldata buffer.\n */\n function _locationInBlock(\n bytes calldata data,\n uint256 location,\n uint256 offset,\n bool isInline\n ) private pure returns (uint256) {\n uint256 headLocation = location + offset;\n if (isInline) {\n return headLocation;\n } else {\n return location + uint256(word(data, headLocation));\n }\n }\n\n /**\n * @dev Plucks a slice of bytes from calldata.\n * @param data The calldata to pluck the slice from.\n * @param location The starting location of the slice.\n * @param size The size of the slice.\n * @return A slice of bytes from calldata.\n */\n function pluck(\n bytes calldata data,\n uint256 location,\n uint256 size\n ) internal pure returns (bytes calldata) {\n return data[location:location + size];\n }\n\n /**\n * @dev Loads a word from calldata.\n * @param data The calldata to load the word from.\n * @param location The starting location of the slice.\n * @return result 32 byte word from calldata.\n */\n function word(\n bytes calldata data,\n uint256 location\n ) internal pure returns (bytes32 result) {\n if (location + 32 > data.length) {\n revert CalldataOutOfBounds();\n }\n assembly {\n result := calldataload(add(data.offset, location))\n }\n }\n\n function _ceil32(uint256 size) private pure returns (uint256) {\n // pad size. Source: http://www.cs.nott.ac.uk/~psarb2/G51MPC/slides/NumberLogic.pdf\n return ((size + 32 - 1) / 32) * 32;\n }\n}\n"
|
|
},
|
|
"contracts/Integrity.sol": {
|
|
"content": "// SPDX-License-Identifier: LGPL-3.0-only\npragma solidity >=0.8.17 <0.9.0;\n\nimport \"./Topology.sol\";\n\n/**\n * @title Integrity, A library that validates condition integrity, and\n * adherence to the expected input structure and rules.\n * @author Cristóvão Honorato - <[email protected]>\n */\nlibrary Integrity {\n error UnsuitableRootNode();\n\n error NotBFS();\n\n error UnsuitableParameterType(uint256 index);\n\n error UnsuitableCompValue(uint256 index);\n\n error UnsupportedOperator(uint256 index);\n\n error UnsuitableParent(uint256 index);\n\n error UnsuitableChildCount(uint256 index);\n\n error UnsuitableChildTypeTree(uint256 index);\n\n function enforce(ConditionFlat[] memory conditions) external pure {\n _root(conditions);\n for (uint256 i = 0; i < conditions.length; ++i) {\n _node(conditions[i], i);\n }\n _tree(conditions);\n }\n\n function _root(ConditionFlat[] memory conditions) private pure {\n uint256 count;\n\n for (uint256 i; i < conditions.length; ++i) {\n if (conditions[i].parent == i) ++count;\n }\n if (count != 1 || conditions[0].parent != 0) {\n revert UnsuitableRootNode();\n }\n }\n\n function _node(ConditionFlat memory condition, uint256 index) private pure {\n Operator operator = condition.operator;\n ParameterType paramType = condition.paramType;\n bytes memory compValue = condition.compValue;\n if (operator == Operator.Pass) {\n if (condition.compValue.length != 0) {\n revert UnsuitableCompValue(index);\n }\n } else if (operator >= Operator.And && operator <= Operator.Nor) {\n if (paramType != ParameterType.None) {\n revert UnsuitableParameterType(index);\n }\n if (condition.compValue.length != 0) {\n revert UnsuitableCompValue(index);\n }\n } else if (operator == Operator.Matches) {\n if (\n paramType != ParameterType.Tuple &&\n paramType != ParameterType.Array &&\n paramType != ParameterType.Calldata &&\n paramType != ParameterType.AbiEncoded\n ) {\n revert UnsuitableParameterType(index);\n }\n if (compValue.length != 0) {\n revert UnsuitableCompValue(index);\n }\n } else if (\n operator == Operator.ArraySome ||\n operator == Operator.ArrayEvery ||\n operator == Operator.ArraySubset\n ) {\n if (paramType != ParameterType.Array) {\n revert UnsuitableParameterType(index);\n }\n if (compValue.length != 0) {\n revert UnsuitableCompValue(index);\n }\n } else if (operator == Operator.EqualToAvatar) {\n if (paramType != ParameterType.Static) {\n revert UnsuitableParameterType(index);\n }\n if (compValue.length != 0) {\n revert UnsuitableCompValue(index);\n }\n } else if (operator == Operator.EqualTo) {\n if (\n paramType != ParameterType.Static &&\n paramType != ParameterType.Dynamic &&\n paramType != ParameterType.Tuple &&\n paramType != ParameterType.Array\n ) {\n revert UnsuitableParameterType(index);\n }\n if (compValue.length == 0 || compValue.length % 32 != 0) {\n revert UnsuitableCompValue(index);\n }\n } else if (\n operator == Operator.GreaterThan ||\n operator == Operator.LessThan ||\n operator == Operator.SignedIntGreaterThan ||\n operator == Operator.SignedIntLessThan\n ) {\n if (paramType != ParameterType.Static) {\n revert UnsuitableParameterType(index);\n }\n if (compValue.length != 32) {\n revert UnsuitableCompValue(index);\n }\n } else if (operator == Operator.Bitmask) {\n if (\n paramType != ParameterType.Static &&\n paramType != ParameterType.Dynamic\n ) {\n revert UnsuitableParameterType(index);\n }\n if (compValue.length != 32) {\n revert UnsuitableCompValue(index);\n }\n } else if (operator == Operator.Custom) {\n if (compValue.length != 32) {\n revert UnsuitableCompValue(index);\n }\n } else if (operator == Operator.WithinAllowance) {\n if (paramType != ParameterType.Static) {\n revert UnsuitableParameterType(index);\n }\n if (compValue.length != 32) {\n revert UnsuitableCompValue(index);\n }\n } else if (\n operator == Operator.EtherWithinAllowance ||\n operator == Operator.CallWithinAllowance\n ) {\n if (paramType != ParameterType.None) {\n revert UnsuitableParameterType(index);\n }\n if (compValue.length != 32) {\n revert UnsuitableCompValue(index);\n }\n } else {\n revert UnsupportedOperator(index);\n }\n }\n\n function _tree(ConditionFlat[] memory conditions) private pure {\n uint256 length = conditions.length;\n // check BFS\n for (uint256 i = 1; i < length; ++i) {\n if (conditions[i - 1].parent > conditions[i].parent) {\n revert NotBFS();\n }\n }\n\n for (uint256 i = 0; i < length; ++i) {\n if (\n (conditions[i].operator == Operator.EtherWithinAllowance ||\n conditions[i].operator == Operator.CallWithinAllowance) &&\n conditions[conditions[i].parent].paramType !=\n ParameterType.Calldata\n ) {\n revert UnsuitableParent(i);\n }\n }\n\n Topology.Bounds[] memory childrenBounds = Topology.childrenBounds(\n conditions\n );\n\n for (uint256 i = 0; i < conditions.length; i++) {\n ConditionFlat memory condition = conditions[i];\n Topology.Bounds memory childBounds = childrenBounds[i];\n\n if (condition.paramType == ParameterType.None) {\n if (\n (condition.operator == Operator.EtherWithinAllowance ||\n condition.operator == Operator.CallWithinAllowance) &&\n childBounds.length != 0\n ) {\n revert UnsuitableChildCount(i);\n }\n if (\n (condition.operator >= Operator.And &&\n condition.operator <= Operator.Nor)\n ) {\n if (childBounds.length == 0) {\n revert UnsuitableChildCount(i);\n }\n }\n } else if (\n condition.paramType == ParameterType.Static ||\n condition.paramType == ParameterType.Dynamic\n ) {\n if (childBounds.length != 0) {\n revert UnsuitableChildCount(i);\n }\n } else if (\n condition.paramType == ParameterType.Tuple ||\n condition.paramType == ParameterType.Calldata ||\n condition.paramType == ParameterType.AbiEncoded\n ) {\n if (childBounds.length == 0) {\n revert UnsuitableChildCount(i);\n }\n } else {\n assert(condition.paramType == ParameterType.Array);\n\n if (childBounds.length == 0) {\n revert UnsuitableChildCount(i);\n }\n\n if (\n (condition.operator == Operator.ArraySome ||\n condition.operator == Operator.ArrayEvery) &&\n childBounds.length != 1\n ) {\n revert UnsuitableChildCount(i);\n } else if (\n condition.operator == Operator.ArraySubset &&\n childBounds.length > 256\n ) {\n revert UnsuitableChildCount(i);\n }\n }\n }\n\n for (uint256 i = 0; i < conditions.length; i++) {\n ConditionFlat memory condition = conditions[i];\n if (\n ((condition.operator >= Operator.And &&\n condition.operator <= Operator.Nor) ||\n condition.paramType == ParameterType.Array) &&\n childrenBounds[i].length > 1\n ) {\n _compatibleSiblingTypes(conditions, i, childrenBounds);\n }\n }\n\n Topology.TypeTree memory typeTree = Topology.typeTree(\n conditions,\n 0,\n childrenBounds\n );\n\n if (typeTree.paramType != ParameterType.Calldata) {\n revert UnsuitableRootNode();\n }\n }\n\n function _compatibleSiblingTypes(\n ConditionFlat[] memory conditions,\n uint256 index,\n Topology.Bounds[] memory childrenBounds\n ) private pure {\n uint256 start = childrenBounds[index].start;\n uint256 end = childrenBounds[index].end;\n\n for (uint256 j = start + 1; j < end; ++j) {\n if (\n !_isTypeMatch(conditions, start, j, childrenBounds) &&\n !_isTypeEquivalent(conditions, start, j, childrenBounds)\n ) {\n revert UnsuitableChildTypeTree(index);\n }\n }\n }\n\n function _isTypeMatch(\n ConditionFlat[] memory conditions,\n uint256 i,\n uint256 j,\n Topology.Bounds[] memory childrenBounds\n ) private pure returns (bool) {\n return\n typeTreeId(Topology.typeTree(conditions, i, childrenBounds)) ==\n typeTreeId(Topology.typeTree(conditions, j, childrenBounds));\n }\n\n function _isTypeEquivalent(\n ConditionFlat[] memory conditions,\n uint256 i,\n uint256 j,\n Topology.Bounds[] memory childrenBounds\n ) private pure returns (bool) {\n ParameterType leftParamType = Topology\n .typeTree(conditions, i, childrenBounds)\n .paramType;\n return\n (leftParamType == ParameterType.Calldata ||\n leftParamType == ParameterType.AbiEncoded) &&\n Topology.typeTree(conditions, j, childrenBounds).paramType ==\n ParameterType.Dynamic;\n }\n\n function typeTreeId(\n Topology.TypeTree memory node\n ) private pure returns (bytes32) {\n uint256 childCount = node.children.length;\n if (childCount > 0) {\n bytes32[] memory ids = new bytes32[](node.children.length);\n for (uint256 i = 0; i < childCount; ++i) {\n ids[i] = typeTreeId(node.children[i]);\n }\n\n return keccak256(abi.encodePacked(node.paramType, \"-\", ids));\n } else {\n return bytes32(uint256(node.paramType));\n }\n }\n}\n"
|
|
},
|
|
"contracts/Periphery.sol": {
|
|
"content": "// SPDX-License-Identifier: LGPL-3.0-only\npragma solidity >=0.8.17 <0.9.0;\n\nimport \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport \"./adapters/Types.sol\";\n\n/**\n * @title Periphery - a coordinating component that facilitates plug-and-play\n * functionality for the Zodiac Roles Mod through the use of adapters.\n * @author Cristóvão Honorato - <[email protected]>\n */\nabstract contract Periphery is OwnableUpgradeable {\n event SetUnwrapAdapter(\n address to,\n bytes4 selector,\n ITransactionUnwrapper adapter\n );\n\n mapping(bytes32 => ITransactionUnwrapper) public unwrappers;\n\n function setTransactionUnwrapper(\n address to,\n bytes4 selector,\n ITransactionUnwrapper adapter\n ) external onlyOwner {\n unwrappers[bytes32(bytes20(to)) | (bytes32(selector) >> 160)] = adapter;\n emit SetUnwrapAdapter(to, selector, adapter);\n }\n\n function getTransactionUnwrapper(\n address to,\n bytes4 selector\n ) internal view returns (ITransactionUnwrapper) {\n return unwrappers[bytes32(bytes20(to)) | (bytes32(selector) >> 160)];\n }\n}\n"
|
|
},
|
|
"contracts/PermissionBuilder.sol": {
|
|
"content": "// SPDX-License-Identifier: LGPL-3.0-only\npragma solidity >=0.8.17 <0.9.0;\n\nimport \"./Core.sol\";\nimport \"./Integrity.sol\";\n\nimport \"./packers/BufferPacker.sol\";\n\n/**\n * @title PermissionBuilder - a component of the Zodiac Roles Mod that is\n * responsible for constructing, managing, granting, and revoking all types\n * of permission data.\n * @author Cristóvão Honorato - <[email protected]>\n * @author Jan-Felix Schwarz - <[email protected]>\n */\nabstract contract PermissionBuilder is Core {\n event AllowTarget(\n bytes32 roleKey,\n address targetAddress,\n ExecutionOptions options\n );\n event RevokeTarget(bytes32 roleKey, address targetAddress);\n event ScopeTarget(bytes32 roleKey, address targetAddress);\n\n event AllowFunction(\n bytes32 roleKey,\n address targetAddress,\n bytes4 selector,\n ExecutionOptions options\n );\n event RevokeFunction(\n bytes32 roleKey,\n address targetAddress,\n bytes4 selector\n );\n event ScopeFunction(\n bytes32 roleKey,\n address targetAddress,\n bytes4 selector,\n ConditionFlat[] conditions,\n ExecutionOptions options\n );\n\n event SetAllowance(\n bytes32 allowanceKey,\n uint128 balance,\n uint128 maxRefill,\n uint128 refill,\n uint64 period,\n uint64 timestamp\n );\n\n /// @dev Allows transactions to a target address.\n /// @param roleKey identifier of the role to be modified.\n /// @param targetAddress Destination address of transaction.\n /// @param options designates if a transaction can send ether and/or delegatecall to target.\n function allowTarget(\n bytes32 roleKey,\n address targetAddress,\n ExecutionOptions options\n ) external onlyOwner {\n roles[roleKey].targets[targetAddress] = TargetAddress({\n clearance: Clearance.Target,\n options: options\n });\n emit AllowTarget(roleKey, targetAddress, options);\n }\n\n /// @dev Removes transactions to a target address.\n /// @param roleKey identifier of the role to be modified.\n /// @param targetAddress Destination address of transaction.\n function revokeTarget(\n bytes32 roleKey,\n address targetAddress\n ) external onlyOwner {\n roles[roleKey].targets[targetAddress] = TargetAddress({\n clearance: Clearance.None,\n options: ExecutionOptions.None\n });\n emit RevokeTarget(roleKey, targetAddress);\n }\n\n /// @dev Designates only specific functions can be called.\n /// @param roleKey identifier of the role to be modified.\n /// @param targetAddress Destination address of transaction.\n function scopeTarget(\n bytes32 roleKey,\n address targetAddress\n ) external onlyOwner {\n roles[roleKey].targets[targetAddress] = TargetAddress({\n clearance: Clearance.Function,\n options: ExecutionOptions.None\n });\n emit ScopeTarget(roleKey, targetAddress);\n }\n\n /// @dev Specifies the functions that can be called.\n /// @param roleKey identifier of the role to be modified.\n /// @param targetAddress Destination address of transaction.\n /// @param selector 4 byte function selector.\n /// @param options designates if a transaction can send ether and/or delegatecall to target.\n function allowFunction(\n bytes32 roleKey,\n address targetAddress,\n bytes4 selector,\n ExecutionOptions options\n ) external onlyOwner {\n roles[roleKey].scopeConfig[_key(targetAddress, selector)] = BufferPacker\n .packHeaderAsWildcarded(options);\n\n emit AllowFunction(roleKey, targetAddress, selector, options);\n }\n\n /// @dev Removes the functions that can be called.\n /// @param roleKey identifier of the role to be modified.\n /// @param targetAddress Destination address of transaction.\n /// @param selector 4 byte function selector.\n function revokeFunction(\n bytes32 roleKey,\n address targetAddress,\n bytes4 selector\n ) external onlyOwner {\n delete roles[roleKey].scopeConfig[_key(targetAddress, selector)];\n emit RevokeFunction(roleKey, targetAddress, selector);\n }\n\n /// @dev Sets conditions to enforce on calls to the specified target.\n /// @param roleKey identifier of the role to be modified.\n /// @param targetAddress Destination address of transaction.\n /// @param selector 4 byte function selector.\n /// @param conditions The conditions to enforce.\n /// @param options designates if a transaction can send ether and/or delegatecall to target.\n function scopeFunction(\n bytes32 roleKey,\n address targetAddress,\n bytes4 selector,\n ConditionFlat[] memory conditions,\n ExecutionOptions options\n ) external onlyOwner {\n Integrity.enforce(conditions);\n\n _store(\n roles[roleKey],\n _key(targetAddress, selector),\n conditions,\n options\n );\n\n emit ScopeFunction(\n roleKey,\n targetAddress,\n selector,\n conditions,\n options\n );\n }\n\n function setAllowance(\n bytes32 key,\n uint128 balance,\n uint128 maxRefill,\n uint128 refill,\n uint64 period,\n uint64 timestamp\n ) external onlyOwner {\n maxRefill = maxRefill != 0 ? maxRefill : type(uint128).max;\n timestamp = timestamp != 0 ? timestamp : uint64(block.timestamp);\n\n allowances[key] = Allowance({\n refill: refill,\n maxRefill: maxRefill,\n period: period,\n timestamp: timestamp,\n balance: balance\n });\n emit SetAllowance(key, balance, maxRefill, refill, period, timestamp);\n }\n}\n"
|
|
},
|
|
"contracts/PermissionChecker.sol": {
|
|
"content": "// SPDX-License-Identifier: LGPL-3.0-only\npragma solidity >=0.8.17 <0.9.0;\n\nimport \"@gnosis.pm/safe-contracts/contracts/common/Enum.sol\";\n\nimport \"./Consumptions.sol\";\nimport \"./Core.sol\";\nimport \"./Decoder.sol\";\nimport \"./Periphery.sol\";\n\nimport \"./packers/BufferPacker.sol\";\n\n/**\n * @title PermissionChecker - a component of Zodiac Roles Mod responsible\n * for enforcing and authorizing actions performed on behalf of a role.\n *\n * @author Cristóvão Honorato - <[email protected]>\n * @author Jan-Felix Schwarz - <[email protected]>\n */\nabstract contract PermissionChecker is Core, Periphery {\n function _authorize(\n bytes32 roleKey,\n address to,\n uint256 value,\n bytes calldata data,\n Enum.Operation operation\n ) internal moduleOnly returns (Consumption[] memory) {\n // We never authorize the zero role, as it could clash with the\n // unassigned default role\n if (roleKey == 0) {\n revert NoMembership();\n }\n\n Role storage role = roles[roleKey];\n if (!role.members[sentOrSignedByModule()]) {\n revert NoMembership();\n }\n\n ITransactionUnwrapper adapter = getTransactionUnwrapper(\n to,\n bytes4(data)\n );\n\n Status status;\n Result memory result;\n if (address(adapter) == address(0)) {\n (status, result) = _transaction(\n role,\n to,\n value,\n data,\n operation,\n result.consumptions\n );\n } else {\n (status, result) = _multiEntrypoint(\n ITransactionUnwrapper(adapter),\n role,\n to,\n value,\n data,\n operation\n );\n }\n if (status != Status.Ok) {\n revert ConditionViolation(status, result.info);\n }\n\n return result.consumptions;\n }\n\n function _multiEntrypoint(\n ITransactionUnwrapper adapter,\n Role storage role,\n address to,\n uint256 value,\n bytes calldata data,\n Enum.Operation operation\n ) private view returns (Status status, Result memory result) {\n try adapter.unwrap(to, value, data, operation) returns (\n UnwrappedTransaction[] memory transactions\n ) {\n for (uint256 i; i < transactions.length; ) {\n UnwrappedTransaction memory transaction = transactions[i];\n uint256 left = transaction.dataLocation;\n uint256 right = left + transaction.dataSize;\n (status, result) = _transaction(\n role,\n transaction.to,\n transaction.value,\n data[left:right],\n transaction.operation,\n result.consumptions\n );\n if (status != Status.Ok) {\n return (status, result);\n }\n unchecked {\n ++i;\n }\n }\n } catch {\n revert MalformedMultiEntrypoint();\n }\n }\n\n /// @dev Inspects an individual transaction and performs checks based on permission scoping.\n /// Wildcarded indicates whether params need to be inspected or not. When true, only ExecutionOptions are checked.\n /// @param role Role to check for.\n /// @param to Destination address of transaction.\n /// @param value Ether value of module transaction.\n /// @param data Data payload of module transaction.\n /// @param operation Operation type of module transaction: 0 == call, 1 == delegate call.\n function _transaction(\n Role storage role,\n address to,\n uint256 value,\n bytes calldata data,\n Enum.Operation operation,\n Consumption[] memory consumptions\n ) private view returns (Status, Result memory) {\n if (data.length != 0 && data.length < 4) {\n revert FunctionSignatureTooShort();\n }\n\n if (role.targets[to].clearance == Clearance.Function) {\n bytes32 key = _key(to, bytes4(data));\n {\n bytes32 header = role.scopeConfig[key];\n if (header == 0) {\n return (\n Status.FunctionNotAllowed,\n Result({\n consumptions: consumptions,\n info: bytes32(bytes4(data))\n })\n );\n }\n\n (bool isWildcarded, ExecutionOptions options) = BufferPacker\n .unpackOptions(header);\n\n Status status = _executionOptions(value, operation, options);\n if (status != Status.Ok) {\n return (\n status,\n Result({consumptions: consumptions, info: 0})\n );\n }\n\n if (isWildcarded) {\n return (\n Status.Ok,\n Result({consumptions: consumptions, info: 0})\n );\n }\n }\n\n return\n _scopedFunction(\n role,\n key,\n data,\n Context({\n to: to,\n value: value,\n operation: operation,\n consumptions: consumptions\n })\n );\n } else if (role.targets[to].clearance == Clearance.Target) {\n return (\n _executionOptions(value, operation, role.targets[to].options),\n Result({consumptions: consumptions, info: 0})\n );\n } else {\n return (\n Status.TargetAddressNotAllowed,\n Result({consumptions: consumptions, info: 0})\n );\n }\n }\n\n /// @dev Examines the ether value and operation for a given role target.\n /// @param value Ether value of module transaction.\n /// @param operation Operation type of module transaction: 0 == call, 1 == delegate call.\n /// @param options Determines if a transaction can send ether and/or delegatecall to target.\n function _executionOptions(\n uint256 value,\n Enum.Operation operation,\n ExecutionOptions options\n ) private pure returns (Status) {\n // isSend && !canSend\n if (\n value > 0 &&\n options != ExecutionOptions.Send &&\n options != ExecutionOptions.Both\n ) {\n return Status.SendNotAllowed;\n }\n\n // isDelegateCall && !canDelegateCall\n if (\n operation == Enum.Operation.DelegateCall &&\n options != ExecutionOptions.DelegateCall &&\n options != ExecutionOptions.Both\n ) {\n return Status.DelegateCallNotAllowed;\n }\n\n return Status.Ok;\n }\n\n function _scopedFunction(\n Role storage role,\n bytes32 key,\n bytes calldata data,\n Context memory context\n ) private view returns (Status, Result memory) {\n (Condition memory condition, Consumption[] memory consumptions) = _load(\n role,\n key\n );\n ParameterPayload memory payload = Decoder.inspect(data, condition);\n\n context.consumptions = context.consumptions.length > 0\n ? Consumptions.merge(context.consumptions, consumptions)\n : consumptions;\n\n return _walk(data, condition, payload, context);\n }\n\n function _walk(\n bytes calldata data,\n Condition memory condition,\n ParameterPayload memory payload,\n Context memory context\n ) private view returns (Status, Result memory) {\n Operator operator = condition.operator;\n\n if (operator < Operator.EqualTo) {\n if (operator == Operator.Pass) {\n return (\n Status.Ok,\n Result({consumptions: context.consumptions, info: 0})\n );\n } else if (operator == Operator.Matches) {\n return _matches(data, condition, payload, context);\n } else if (operator == Operator.And) {\n return _and(data, condition, payload, context);\n } else if (operator == Operator.Or) {\n return _or(data, condition, payload, context);\n } else if (operator == Operator.Nor) {\n return _nor(data, condition, payload, context);\n } else if (operator == Operator.ArraySome) {\n return _arraySome(data, condition, payload, context);\n } else if (operator == Operator.ArrayEvery) {\n return _arrayEvery(data, condition, payload, context);\n } else {\n assert(operator == Operator.ArraySubset);\n return _arraySubset(data, condition, payload, context);\n }\n } else {\n if (operator <= Operator.LessThan) {\n return (\n _compare(data, condition, payload),\n Result({consumptions: context.consumptions, info: 0})\n );\n } else if (operator <= Operator.SignedIntLessThan) {\n return (\n _compareSignedInt(data, condition, payload),\n Result({consumptions: context.consumptions, info: 0})\n );\n } else if (operator == Operator.Bitmask) {\n return (\n _bitmask(data, condition, payload),\n Result({consumptions: context.consumptions, info: 0})\n );\n } else if (operator == Operator.Custom) {\n return _custom(data, condition, payload, context);\n } else if (operator == Operator.WithinAllowance) {\n return _withinAllowance(data, condition, payload, context);\n } else if (operator == Operator.EtherWithinAllowance) {\n return _etherWithinAllowance(condition, context);\n } else {\n assert(operator == Operator.CallWithinAllowance);\n return _callWithinAllowance(condition, context);\n }\n }\n }\n\n function _matches(\n bytes calldata data,\n Condition memory condition,\n ParameterPayload memory payload,\n Context memory context\n ) private view returns (Status status, Result memory result) {\n result.consumptions = context.consumptions;\n\n if (condition.children.length != payload.children.length) {\n return (Status.ParameterNotAMatch, result);\n }\n\n for (uint256 i; i < condition.children.length; ) {\n (status, result) = _walk(\n data,\n condition.children[i],\n payload.children[i],\n Context({\n to: context.to,\n value: context.value,\n operation: context.operation,\n consumptions: result.consumptions\n })\n );\n if (status != Status.Ok) {\n return (\n status,\n Result({\n consumptions: context.consumptions,\n info: result.info\n })\n );\n }\n unchecked {\n ++i;\n }\n }\n\n return (Status.Ok, result);\n }\n\n function _and(\n bytes calldata data,\n Condition memory condition,\n ParameterPayload memory payload,\n Context memory context\n ) private view returns (Status status, Result memory result) {\n result.consumptions = context.consumptions;\n\n for (uint256 i; i < condition.children.length; ) {\n (status, result) = _walk(\n data,\n condition.children[i],\n payload,\n Context({\n to: context.to,\n value: context.value,\n operation: context.operation,\n consumptions: result.consumptions\n })\n );\n if (status != Status.Ok) {\n return (\n status,\n Result({\n consumptions: context.consumptions,\n info: result.info\n })\n );\n }\n unchecked {\n ++i;\n }\n }\n return (Status.Ok, result);\n }\n\n function _or(\n bytes calldata data,\n Condition memory condition,\n ParameterPayload memory payload,\n Context memory context\n ) private view returns (Status status, Result memory result) {\n result.consumptions = context.consumptions;\n\n for (uint256 i; i < condition.children.length; ) {\n (status, result) = _walk(\n data,\n condition.children[i],\n payload,\n Context({\n to: context.to,\n value: context.value,\n operation: context.operation,\n consumptions: result.consumptions\n })\n );\n if (status == Status.Ok) {\n return (status, result);\n }\n unchecked {\n ++i;\n }\n }\n\n return (\n Status.OrViolation,\n Result({consumptions: context.consumptions, info: 0})\n );\n }\n\n function _nor(\n bytes calldata data,\n Condition memory condition,\n ParameterPayload memory payload,\n Context memory context\n ) private view returns (Status status, Result memory) {\n for (uint256 i; i < condition.children.length; ) {\n (status, ) = _walk(data, condition.children[i], payload, context);\n if (status == Status.Ok) {\n return (\n Status.NorViolation,\n Result({consumptions: context.consumptions, info: 0})\n );\n }\n unchecked {\n ++i;\n }\n }\n return (\n Status.Ok,\n Result({consumptions: context.consumptions, info: 0})\n );\n }\n\n function _arraySome(\n bytes calldata data,\n Condition memory condition,\n ParameterPayload memory payload,\n Context memory context\n ) private view returns (Status status, Result memory result) {\n result.consumptions = context.consumptions;\n\n uint256 length = payload.children.length;\n for (uint256 i; i < length; ) {\n (status, result) = _walk(\n data,\n condition.children[0],\n payload.children[i],\n Context({\n to: context.to,\n value: context.value,\n operation: context.operation,\n consumptions: context.consumptions\n })\n );\n if (status == Status.Ok) {\n return (status, result);\n }\n unchecked {\n ++i;\n }\n }\n return (\n Status.NoArrayElementPasses,\n Result({consumptions: context.consumptions, info: 0})\n );\n }\n\n function _arrayEvery(\n bytes calldata data,\n Condition memory condition,\n ParameterPayload memory payload,\n Context memory context\n ) private view returns (Status status, Result memory result) {\n result.consumptions = context.consumptions;\n\n for (uint256 i; i < payload.children.length; ) {\n (status, result) = _walk(\n data,\n condition.children[0],\n payload.children[i],\n Context({\n to: context.to,\n value: context.value,\n operation: context.operation,\n consumptions: result.consumptions\n })\n );\n if (status != Status.Ok) {\n return (\n Status.NotEveryArrayElementPasses,\n Result({consumptions: context.consumptions, info: 0})\n );\n }\n unchecked {\n ++i;\n }\n }\n return (Status.Ok, result);\n }\n\n function _arraySubset(\n bytes calldata data,\n Condition memory condition,\n ParameterPayload memory payload,\n Context memory context\n ) private view returns (Status, Result memory result) {\n result.consumptions = context.consumptions;\n\n if (\n payload.children.length == 0 ||\n payload.children.length > condition.children.length\n ) {\n return (Status.ParameterNotSubsetOfAllowed, result);\n }\n\n uint256 taken;\n for (uint256 i; i < payload.children.length; ++i) {\n bool found = false;\n for (uint256 j; j < condition.children.length; ++j) {\n if (taken & (1 << j) != 0) continue;\n\n (Status status, Result memory _result) = _walk(\n data,\n condition.children[j],\n payload.children[i],\n Context({\n to: context.to,\n value: context.value,\n operation: context.operation,\n consumptions: result.consumptions\n })\n );\n if (status == Status.Ok) {\n found = true;\n taken |= 1 << j;\n result = _result;\n break;\n }\n }\n if (!found) {\n return (\n Status.ParameterNotSubsetOfAllowed,\n Result({consumptions: context.consumptions, info: 0})\n );\n }\n }\n\n return (Status.Ok, result);\n }\n\n function _compare(\n bytes calldata data,\n Condition memory condition,\n ParameterPayload memory payload\n ) private pure returns (Status) {\n Operator operator = condition.operator;\n bytes32 compValue = condition.compValue;\n bytes32 value = operator == Operator.EqualTo\n ? keccak256(Decoder.pluck(data, payload.location, payload.size))\n : Decoder.word(data, payload.location);\n\n if (operator == Operator.EqualTo && value != compValue) {\n return Status.ParameterNotAllowed;\n } else if (operator == Operator.GreaterThan && value <= compValue) {\n return Status.ParameterLessThanAllowed;\n } else if (operator == Operator.LessThan && value >= compValue) {\n return Status.ParameterGreaterThanAllowed;\n } else {\n return Status.Ok;\n }\n }\n\n function _compareSignedInt(\n bytes calldata data,\n Condition memory condition,\n ParameterPayload memory payload\n ) private pure returns (Status) {\n Operator operator = condition.operator;\n int256 compValue = int256(uint256(condition.compValue));\n int256 value = int256(uint256(Decoder.word(data, payload.location)));\n\n if (operator == Operator.SignedIntGreaterThan && value <= compValue) {\n return Status.ParameterLessThanAllowed;\n } else if (\n operator == Operator.SignedIntLessThan && value >= compValue\n ) {\n return Status.ParameterGreaterThanAllowed;\n } else {\n return Status.Ok;\n }\n }\n\n /**\n * Applies a shift and bitmask on the payload bytes and compares the\n * result to the expected value. The shift offset, bitmask, and expected\n * value are specified in the compValue parameter, which is tightly\n * packed as follows:\n * <2 bytes shift offset><15 bytes bitmask><15 bytes expected value>\n */\n function _bitmask(\n bytes calldata data,\n Condition memory condition,\n ParameterPayload memory payload\n ) private pure returns (Status) {\n bytes32 compValue = condition.compValue;\n bool isInline = condition.paramType == ParameterType.Static;\n bytes calldata value = Decoder.pluck(\n data,\n payload.location + (isInline ? 0 : 32),\n isInline\n ? payload.size\n : uint256(Decoder.word(data, payload.location))\n );\n\n uint256 shift = uint16(bytes2(compValue));\n if (shift >= value.length) {\n return Status.BitmaskOverflow;\n }\n\n bytes32 rinse = bytes15(0xffffffffffffffffffffffffffffff);\n bytes32 mask = (compValue << 16) & rinse;\n // while its necessary to apply the rinse to the mask its not strictly\n // necessary to do so for the expected value, since we get remaining\n // 15 bytes anyway (shifting the word by 17 bytes)\n bytes32 expected = (compValue << (16 + 15 * 8)) & rinse;\n bytes32 slice = bytes32(value[shift:]);\n\n return\n (slice & mask) == expected ? Status.Ok : Status.BitmaskNotAllowed;\n }\n\n function _custom(\n bytes calldata data,\n Condition memory condition,\n ParameterPayload memory payload,\n Context memory context\n ) private view returns (Status, Result memory) {\n // 20 bytes on the left\n ICustomCondition adapter = ICustomCondition(\n address(bytes20(condition.compValue))\n );\n // 12 bytes on the right\n bytes12 extra = bytes12(uint96(uint256(condition.compValue)));\n\n (bool success, bytes32 info) = adapter.check(\n context.to,\n context.value,\n data,\n context.operation,\n payload.location,\n payload.size,\n extra\n );\n return (\n success ? Status.Ok : Status.CustomConditionViolation,\n Result({consumptions: context.consumptions, info: info})\n );\n }\n\n function _withinAllowance(\n bytes calldata data,\n Condition memory condition,\n ParameterPayload memory payload,\n Context memory context\n ) private pure returns (Status, Result memory) {\n uint256 value = uint256(Decoder.word(data, payload.location));\n return __consume(value, condition, context.consumptions);\n }\n\n function _etherWithinAllowance(\n Condition memory condition,\n Context memory context\n ) private pure returns (Status status, Result memory result) {\n (status, result) = __consume(\n context.value,\n condition,\n context.consumptions\n );\n return (\n status == Status.Ok ? Status.Ok : Status.EtherAllowanceExceeded,\n result\n );\n }\n\n function _callWithinAllowance(\n Condition memory condition,\n Context memory context\n ) private pure returns (Status status, Result memory result) {\n (status, result) = __consume(1, condition, context.consumptions);\n return (\n status == Status.Ok ? Status.Ok : Status.CallAllowanceExceeded,\n result\n );\n }\n\n function __consume(\n uint256 value,\n Condition memory condition,\n Consumption[] memory consumptions\n ) private pure returns (Status, Result memory) {\n (uint256 index, bool found) = Consumptions.find(\n consumptions,\n condition.compValue\n );\n assert(found);\n\n if (\n value + consumptions[index].consumed > consumptions[index].balance\n ) {\n return (\n Status.AllowanceExceeded,\n Result({\n consumptions: consumptions,\n info: consumptions[index].allowanceKey\n })\n );\n } else {\n consumptions = Consumptions.clone(consumptions);\n consumptions[index].consumed += uint128(value);\n return (Status.Ok, Result({consumptions: consumptions, info: 0}));\n }\n }\n\n struct Context {\n address to;\n uint256 value;\n Consumption[] consumptions;\n Enum.Operation operation;\n }\n\n struct Result {\n Consumption[] consumptions;\n bytes32 info;\n }\n\n enum Status {\n Ok,\n /// Role not allowed to delegate call to target address\n DelegateCallNotAllowed,\n /// Role not allowed to call target address\n TargetAddressNotAllowed,\n /// Role not allowed to call this function on target address\n FunctionNotAllowed,\n /// Role not allowed to send to target address\n SendNotAllowed,\n /// Or conition not met\n OrViolation,\n /// Nor conition not met\n NorViolation,\n /// Parameter value is not equal to allowed\n ParameterNotAllowed,\n /// Parameter value less than allowed\n ParameterLessThanAllowed,\n /// Parameter value greater than maximum allowed by role\n ParameterGreaterThanAllowed,\n /// Parameter value does not match\n ParameterNotAMatch,\n /// Array elements do not meet allowed criteria for every element\n NotEveryArrayElementPasses,\n /// Array elements do not meet allowed criteria for at least one element\n NoArrayElementPasses,\n /// Parameter value not a subset of allowed\n ParameterNotSubsetOfAllowed,\n /// Bitmask exceeded value length\n BitmaskOverflow,\n /// Bitmask not an allowed value\n BitmaskNotAllowed,\n CustomConditionViolation,\n AllowanceExceeded,\n CallAllowanceExceeded,\n EtherAllowanceExceeded\n }\n\n /// Sender is not a member of the role\n error NoMembership();\n\n /// Function signature too short\n error FunctionSignatureTooShort();\n\n /// Calldata unwrapping failed\n error MalformedMultiEntrypoint();\n\n error ConditionViolation(Status status, bytes32 info);\n}\n"
|
|
},
|
|
"contracts/PermissionLoader.sol": {
|
|
"content": "// SPDX-License-Identifier: LGPL-3.0-only\npragma solidity >=0.8.17 <0.9.0;\n\nimport \"@gnosis.pm/zodiac/contracts/core/Modifier.sol\";\nimport \"./Consumptions.sol\";\nimport \"./Core.sol\";\nimport \"./Topology.sol\";\nimport \"./WriteOnce.sol\";\n\nimport \"./packers/Packer.sol\";\n\n/**\n * @title PermissionLoader - a component of the Zodiac Roles Mod that handles\n * the writing and reading of permission data to and from storage.\n * @author Cristóvão Honorato - <[email protected]>\n * @author Jan-Felix Schwarz - <[email protected]>\n */\nabstract contract PermissionLoader is Core {\n function _store(\n Role storage role,\n bytes32 key,\n ConditionFlat[] memory conditions,\n ExecutionOptions options\n ) internal override {\n bytes memory buffer = Packer.pack(conditions);\n address pointer = WriteOnce.store(buffer);\n\n role.scopeConfig[key] = BufferPacker.packHeader(\n conditions.length,\n options,\n pointer\n );\n }\n\n function _load(\n Role storage role,\n bytes32 key\n )\n internal\n view\n override\n returns (Condition memory condition, Consumption[] memory consumptions)\n {\n (uint256 count, address pointer) = BufferPacker.unpackHeader(\n role.scopeConfig[key]\n );\n bytes memory buffer = WriteOnce.load(pointer);\n (\n ConditionFlat[] memory conditionsFlat,\n bytes32[] memory compValues\n ) = BufferPacker.unpackBody(buffer, count);\n\n uint256 allowanceCount;\n\n for (uint256 i; i < conditionsFlat.length; ) {\n Operator operator = conditionsFlat[i].operator;\n if (operator >= Operator.WithinAllowance) {\n ++allowanceCount;\n } else if (operator == Operator.EqualToAvatar) {\n // patch Operator.EqualToAvatar which in reality works as\n // a placeholder\n conditionsFlat[i].operator = Operator.EqualTo;\n compValues[i] = keccak256(abi.encode(avatar));\n }\n unchecked {\n ++i;\n }\n }\n\n _conditionTree(\n conditionsFlat,\n compValues,\n Topology.childrenBounds(conditionsFlat),\n 0,\n condition\n );\n\n return (\n condition,\n allowanceCount > 0\n ? _consumptions(conditionsFlat, compValues, allowanceCount)\n : consumptions\n );\n }\n\n function _conditionTree(\n ConditionFlat[] memory conditionsFlat,\n bytes32[] memory compValues,\n Topology.Bounds[] memory childrenBounds,\n uint256 index,\n Condition memory treeNode\n ) private pure {\n // This function populates a buffer received as an argument instead of\n // instantiating a result object. This is an important gas optimization\n\n ConditionFlat memory conditionFlat = conditionsFlat[index];\n treeNode.paramType = conditionFlat.paramType;\n treeNode.operator = conditionFlat.operator;\n treeNode.compValue = compValues[index];\n\n if (childrenBounds[index].length == 0) {\n return;\n }\n\n uint256 start = childrenBounds[index].start;\n uint256 length = childrenBounds[index].length;\n\n treeNode.children = new Condition[](length);\n for (uint j; j < length; ) {\n _conditionTree(\n conditionsFlat,\n compValues,\n childrenBounds,\n start + j,\n treeNode.children[j]\n );\n unchecked {\n ++j;\n }\n }\n }\n\n function _consumptions(\n ConditionFlat[] memory conditions,\n bytes32[] memory compValues,\n uint256 maxAllowanceCount\n ) private view returns (Consumption[] memory result) {\n uint256 count = conditions.length;\n result = new Consumption[](maxAllowanceCount);\n\n uint256 insert;\n\n for (uint256 i; i < count; ++i) {\n if (conditions[i].operator < Operator.WithinAllowance) {\n continue;\n }\n\n bytes32 key = compValues[i];\n (, bool contains) = Consumptions.find(result, key);\n if (contains) {\n continue;\n }\n\n result[insert].allowanceKey = key;\n (result[insert].balance, ) = _accruedAllowance(\n allowances[key],\n uint64(block.timestamp)\n );\n insert++;\n }\n\n if (insert < maxAllowanceCount) {\n assembly {\n mstore(result, insert)\n }\n }\n }\n}\n"
|
|
},
|
|
"contracts/Roles.sol": {
|
|
"content": "// SPDX-License-Identifier: LGPL-3.0-only\npragma solidity >=0.8.17 <0.9.0;\n\nimport \"./AllowanceTracker.sol\";\nimport \"./PermissionBuilder.sol\";\nimport \"./PermissionChecker.sol\";\nimport \"./PermissionLoader.sol\";\n\n/**\n * @title Zodiac Roles Mod - granular, role-based, access control for your\n * on-chain avatar accounts (like Safe).\n * @author Cristóvão Honorato - <[email protected]>\n * @author Jan-Felix Schwarz - <[email protected]>\n * @author Auryn Macmillan - <[email protected]>\n * @author Nathan Ginnever - <[email protected]>\n */\ncontract Roles is\n Modifier,\n AllowanceTracker,\n PermissionBuilder,\n PermissionChecker,\n PermissionLoader\n{\n mapping(address => bytes32) public defaultRoles;\n\n event AssignRoles(address module, bytes32[] roleKeys, bool[] memberOf);\n event RolesModSetup(\n address indexed initiator,\n address indexed owner,\n address indexed avatar,\n address target\n );\n event SetDefaultRole(address module, bytes32 defaultRoleKey);\n\n error ArraysDifferentLength();\n\n /// Sender is allowed to make this call, but the internal transaction failed\n error ModuleTransactionFailed();\n\n /// @param _owner Address of the owner\n /// @param _avatar Address of the avatar (e.g. a Gnosis Safe)\n /// @param _target Address of the contract that will call exec function\n constructor(address _owner, address _avatar, address _target) {\n bytes memory initParams = abi.encode(_owner, _avatar, _target);\n setUp(initParams);\n }\n\n /// @dev There is no zero address check as solidty will check for\n /// missing arguments and the space of invalid addresses is too large\n /// to check. Invalid avatar or target address can be reset by owner.\n function setUp(bytes memory initParams) public override initializer {\n (address _owner, address _avatar, address _target) = abi.decode(\n initParams,\n (address, address, address)\n );\n _transferOwnership(_owner);\n avatar = _avatar;\n target = _target;\n\n setupModules();\n\n emit RolesModSetup(msg.sender, _owner, _avatar, _target);\n }\n\n /// @dev Assigns and revokes roles to a given module.\n /// @param module Module on which to assign/revoke roles.\n /// @param roleKeys Roles to assign/revoke.\n /// @param memberOf Assign (true) or revoke (false) corresponding roleKeys.\n function assignRoles(\n address module,\n bytes32[] calldata roleKeys,\n bool[] calldata memberOf\n ) external onlyOwner {\n if (roleKeys.length != memberOf.length) {\n revert ArraysDifferentLength();\n }\n for (uint16 i; i < roleKeys.length; ++i) {\n roles[roleKeys[i]].members[module] = memberOf[i];\n }\n if (!isModuleEnabled(module)) {\n enableModule(module);\n }\n emit AssignRoles(module, roleKeys, memberOf);\n }\n\n /// @dev Sets the default role used for a module if it calls execTransactionFromModule() or execTransactionFromModuleReturnData().\n /// @param module Address of the module on which to set default role.\n /// @param roleKey Role to be set as default.\n function setDefaultRole(\n address module,\n bytes32 roleKey\n ) external onlyOwner {\n defaultRoles[module] = roleKey;\n emit SetDefaultRole(module, roleKey);\n }\n\n /// @dev Passes a transaction to the modifier.\n /// @param to Destination address of module transaction\n /// @param value Ether value of module transaction\n /// @param data Data payload of module transaction\n /// @param operation Operation type of module transaction\n /// @notice Can only be called by enabled modules\n function execTransactionFromModule(\n address to,\n uint256 value,\n bytes calldata data,\n Enum.Operation operation\n ) public override returns (bool success) {\n Consumption[] memory consumptions = _authorize(\n defaultRoles[msg.sender],\n to,\n value,\n data,\n operation\n );\n _flushPrepare(consumptions);\n success = exec(to, value, data, operation);\n _flushCommit(consumptions, success);\n }\n\n /// @dev Passes a transaction to the modifier, expects return data.\n /// @param to Destination address of module transaction\n /// @param value Ether value of module transaction\n /// @param data Data payload of module transaction\n /// @param operation Operation type of module transaction\n /// @notice Can only be called by enabled modules\n function execTransactionFromModuleReturnData(\n address to,\n uint256 value,\n bytes calldata data,\n Enum.Operation operation\n ) public override returns (bool success, bytes memory returnData) {\n Consumption[] memory consumptions = _authorize(\n defaultRoles[msg.sender],\n to,\n value,\n data,\n operation\n );\n _flushPrepare(consumptions);\n (success, returnData) = execAndReturnData(to, value, data, operation);\n _flushCommit(consumptions, success);\n }\n\n /// @dev Passes a transaction to the modifier assuming the specified role.\n /// @param to Destination address of module transaction\n /// @param value Ether value of module transaction\n /// @param data Data payload of module transaction\n /// @param operation Operation type of module transaction\n /// @param roleKey Identifier of the role to assume for this transaction\n /// @param shouldRevert Should the function revert on inner execution returning success false?\n /// @notice Can only be called by enabled modules\n function execTransactionWithRole(\n address to,\n uint256 value,\n bytes calldata data,\n Enum.Operation operation,\n bytes32 roleKey,\n bool shouldRevert\n ) public returns (bool success) {\n Consumption[] memory consumptions = _authorize(\n roleKey,\n to,\n value,\n data,\n operation\n );\n _flushPrepare(consumptions);\n success = exec(to, value, data, operation);\n if (shouldRevert && !success) {\n revert ModuleTransactionFailed();\n }\n _flushCommit(consumptions, success);\n }\n\n /// @dev Passes a transaction to the modifier assuming the specified role. Expects return data.\n /// @param to Destination address of module transaction\n /// @param value Ether value of module transaction\n /// @param data Data payload of module transaction\n /// @param operation Operation type of module transaction\n /// @param roleKey Identifier of the role to assume for this transaction\n /// @param shouldRevert Should the function revert on inner execution returning success false?\n /// @notice Can only be called by enabled modules\n function execTransactionWithRoleReturnData(\n address to,\n uint256 value,\n bytes calldata data,\n Enum.Operation operation,\n bytes32 roleKey,\n bool shouldRevert\n ) public returns (bool success, bytes memory returnData) {\n Consumption[] memory consumptions = _authorize(\n roleKey,\n to,\n value,\n data,\n operation\n );\n _flushPrepare(consumptions);\n (success, returnData) = execAndReturnData(to, value, data, operation);\n if (shouldRevert && !success) {\n revert ModuleTransactionFailed();\n }\n _flushCommit(consumptions, success);\n }\n}\n"
|
|
},
|
|
"contracts/Topology.sol": {
|
|
"content": "// SPDX-License-Identifier: LGPL-3.0-only\npragma solidity >=0.8.17 <0.9.0;\n\nimport \"./Types.sol\";\n\n/**\n * @title Topology - a library that provides helper functions for dealing with\n * the flat representation of conditions.\n * @author Cristóvão Honorato - <[email protected]>\n */\nlibrary Topology {\n struct TypeTree {\n ParameterType paramType;\n TypeTree[] children;\n }\n\n struct Bounds {\n uint256 start;\n uint256 end;\n uint256 length;\n }\n\n function childrenBounds(\n ConditionFlat[] memory conditions\n ) internal pure returns (Bounds[] memory result) {\n uint256 count = conditions.length;\n assert(count > 0);\n\n // parents are breadth-first\n result = new Bounds[](count);\n result[0].start = type(uint256).max;\n\n // first item is the root\n for (uint256 i = 1; i < count; ) {\n result[i].start = type(uint256).max;\n Bounds memory parentBounds = result[conditions[i].parent];\n if (parentBounds.start == type(uint256).max) {\n parentBounds.start = i;\n }\n parentBounds.end = i + 1;\n parentBounds.length = parentBounds.end - parentBounds.start;\n unchecked {\n ++i;\n }\n }\n }\n\n function isInline(TypeTree memory node) internal pure returns (bool) {\n ParameterType paramType = node.paramType;\n if (paramType == ParameterType.Static) {\n return true;\n } else if (\n paramType == ParameterType.Dynamic ||\n paramType == ParameterType.Array ||\n paramType == ParameterType.Calldata ||\n paramType == ParameterType.AbiEncoded\n ) {\n return false;\n } else {\n uint256 length = node.children.length;\n\n for (uint256 i; i < length; ) {\n if (!isInline(node.children[i])) {\n return false;\n }\n unchecked {\n ++i;\n }\n }\n return true;\n }\n }\n\n function typeTree(\n Condition memory condition\n ) internal pure returns (TypeTree memory result) {\n if (\n condition.operator >= Operator.And &&\n condition.operator <= Operator.Nor\n ) {\n assert(condition.children.length > 0);\n return typeTree(condition.children[0]);\n }\n\n result.paramType = condition.paramType;\n if (condition.children.length > 0) {\n uint256 length = condition.paramType == ParameterType.Array\n ? 1\n : condition.children.length;\n result.children = new TypeTree[](length);\n\n for (uint256 i; i < length; ) {\n result.children[i] = typeTree(condition.children[i]);\n\n unchecked {\n ++i;\n }\n }\n }\n }\n\n function typeTree(\n ConditionFlat[] memory conditions,\n uint256 index,\n Bounds[] memory bounds\n ) internal pure returns (TypeTree memory result) {\n ConditionFlat memory condition = conditions[index];\n if (\n condition.operator >= Operator.And &&\n condition.operator <= Operator.Nor\n ) {\n assert(bounds[index].length > 0);\n return typeTree(conditions, bounds[index].start, bounds);\n }\n\n result.paramType = condition.paramType;\n if (bounds[index].length > 0) {\n uint256 start = bounds[index].start;\n uint256 end = condition.paramType == ParameterType.Array\n ? bounds[index].start + 1\n : bounds[index].end;\n result.children = new TypeTree[](end - start);\n for (uint256 i = start; i < end; ) {\n result.children[i - start] = typeTree(conditions, i, bounds);\n unchecked {\n ++i;\n }\n }\n }\n }\n}\n"
|
|
},
|
|
"contracts/Types.sol": {
|
|
"content": "// SPDX-License-Identifier: LGPL-3.0-only\npragma solidity >=0.8.17 <0.9.0;\n\n/**\n * @title Types - a file that contains all of the type definitions used throughout\n * the Zodiac Roles Mod.\n * @author Cristóvão Honorato - <[email protected]>\n * @author Jan-Felix Schwarz - <[email protected]>\n */\n\nenum ParameterType {\n None,\n Static,\n Dynamic,\n Tuple,\n Array,\n Calldata,\n AbiEncoded\n}\n\nenum Operator {\n // 00: EMPTY EXPRESSION (default, always passes)\n // paramType: Static / Dynamic / Tuple / Array\n // ❓ children (only for paramType: Tuple / Array to describe their structure)\n // 🚫 compValue\n /* 00: */ Pass,\n // ------------------------------------------------------------\n // 01-04: LOGICAL EXPRESSIONS\n // paramType: None\n // ✅ children\n // 🚫 compValue\n /* 01: */ And,\n /* 02: */ Or,\n /* 03: */ Nor,\n /* 04: */ _Placeholder04,\n // ------------------------------------------------------------\n // 05-14: COMPLEX EXPRESSIONS\n // paramType: Calldata / AbiEncoded / Tuple / Array,\n // ✅ children\n // 🚫 compValue\n /* 05: */ Matches,\n /* 06: */ ArraySome,\n /* 07: */ ArrayEvery,\n /* 08: */ ArraySubset,\n /* 09: */ _Placeholder09,\n /* 10: */ _Placeholder10,\n /* 11: */ _Placeholder11,\n /* 12: */ _Placeholder12,\n /* 13: */ _Placeholder13,\n /* 14: */ _Placeholder14,\n // ------------------------------------------------------------\n // 15: SPECIAL COMPARISON (without compValue)\n // paramType: Static\n // 🚫 children\n // 🚫 compValue\n /* 15: */ EqualToAvatar,\n // ------------------------------------------------------------\n // 16-31: COMPARISON EXPRESSIONS\n // paramType: Static / Dynamic / Tuple / Array\n // ❓ children (only for paramType: Tuple / Array to describe their structure)\n // ✅ compValue\n /* 16: */ EqualTo, // paramType: Static / Dynamic / Tuple / Array\n /* 17: */ GreaterThan, // paramType: Static\n /* 18: */ LessThan, // paramType: Static\n /* 19: */ SignedIntGreaterThan, // paramType: Static\n /* 20: */ SignedIntLessThan, // paramType: Static\n /* 21: */ Bitmask, // paramType: Static / Dynamic\n /* 22: */ Custom, // paramType: Static / Dynamic / Tuple / Array\n /* 23: */ _Placeholder23,\n /* 24: */ _Placeholder24,\n /* 25: */ _Placeholder25,\n /* 26: */ _Placeholder26,\n /* 27: */ _Placeholder27,\n /* 28: */ WithinAllowance, // paramType: Static\n /* 29: */ EtherWithinAllowance, // paramType: None\n /* 30: */ CallWithinAllowance, // paramType: None\n /* 31: */ _Placeholder31\n}\n\nenum ExecutionOptions {\n None,\n Send,\n DelegateCall,\n Both\n}\n\nenum Clearance {\n None,\n Target,\n Function\n}\n\n// This struct is a flattened version of Condition\n// used for ABI encoding a scope config tree\n// (ABI does not support recursive types)\nstruct ConditionFlat {\n uint8 parent;\n ParameterType paramType;\n Operator operator;\n bytes compValue;\n}\n\nstruct Condition {\n ParameterType paramType;\n Operator operator;\n bytes32 compValue;\n Condition[] children;\n}\nstruct ParameterPayload {\n uint256 location;\n uint256 size;\n ParameterPayload[] children;\n}\n\nstruct TargetAddress {\n Clearance clearance;\n ExecutionOptions options;\n}\n\nstruct Role {\n mapping(address => bool) members;\n mapping(address => TargetAddress) targets;\n mapping(bytes32 => bytes32) scopeConfig;\n}\n\n/// @notice The order of members in the `Allowance` struct is significant; members updated during accrual (`balance` and `timestamp`) should be stored in the same word.\n/// @custom:member refill Amount added to balance after each period elapses.\n/// @custom:member maxRefill Refilling stops when balance reaches this value.\n/// @custom:member period Duration, in seconds, before a refill occurs. If set to 0, the allowance is for one-time use and won't be replenished.\n/// @custom:member balance Remaining allowance available for use. Decreases with usage and increases after each refill by the specified refill amount.\n/// @custom:member timestamp Timestamp when the last refill occurred.\nstruct Allowance {\n uint128 refill;\n uint128 maxRefill;\n uint64 period;\n uint128 balance;\n uint64 timestamp;\n}\n\nstruct Consumption {\n bytes32 allowanceKey;\n uint128 balance;\n uint128 consumed;\n}\n"
|
|
},
|
|
"contracts/WriteOnce.sol": {
|
|
"content": "// SPDX-License-Identifier: LGPL-3.0-only\npragma solidity >=0.8.17 <0.9.0;\n\ninterface ISingletonFactory {\n function deploy(\n bytes memory initCode,\n bytes32 salt\n ) external returns (address);\n}\n\nlibrary WriteOnce {\n address public constant SINGLETON_FACTORY =\n 0xce0042B868300000d44A59004Da54A005ffdcf9f;\n\n bytes32 public constant SALT =\n 0x0000000000000000000000000000000000000000000000000000000000000000;\n\n /**\n @notice Stores `data` and returns `pointer` as key for later retrieval\n @dev The pointer is a contract address with `data` as code\n @param data to be written\n @return pointer Pointer to the written `data`\n */\n function store(bytes memory data) internal returns (address pointer) {\n bytes memory creationBytecode = creationBytecodeFor(data);\n pointer = addressFor(creationBytecode);\n\n uint256 size;\n assembly {\n size := extcodesize(pointer)\n }\n\n if (size == 0) {\n assert(\n pointer ==\n ISingletonFactory(SINGLETON_FACTORY).deploy(\n creationBytecode,\n SALT\n )\n );\n }\n }\n\n /**\n @notice Reads the contents of the `pointer` code as data, skips the first byte\n @dev The function is intended for reading pointers generated by `store`\n @param pointer to be read\n @return runtimeBytecode read from `pointer` contract\n */\n function load(\n address pointer\n ) internal view returns (bytes memory runtimeBytecode) {\n uint256 rawSize;\n assembly {\n rawSize := extcodesize(pointer)\n }\n assert(rawSize > 1);\n\n // jump over the prepended 00\n uint256 offset = 1;\n // don't count with the 00\n uint256 size = rawSize - 1;\n\n runtimeBytecode = new bytes(size);\n assembly {\n extcodecopy(pointer, add(runtimeBytecode, 32), offset, size)\n }\n }\n\n function addressFor(\n bytes memory creationBytecode\n ) private pure returns (address) {\n bytes32 hash = keccak256(\n abi.encodePacked(\n bytes1(0xff),\n SINGLETON_FACTORY,\n SALT,\n keccak256(creationBytecode)\n )\n );\n // get the right most 20 bytes\n return address(uint160(uint256(hash)));\n }\n\n /**\n @notice Generate a creation code that results on a contract with `data` as bytecode\n @param data the buffer to be stored\n @return creationBytecode (constructor) for new contract\n */\n function creationBytecodeFor(\n bytes memory data\n ) private pure returns (bytes memory) {\n /*\n 0x00 0x63 0x63XXXXXX PUSH4 _code.length size\n 0x01 0x80 0x80 DUP1 size size\n 0x02 0x60 0x600e PUSH1 14 14 size size\n 0x03 0x60 0x6000 PUSH1 00 0 14 size size\n 0x04 0x39 0x39 CODECOPY size\n 0x05 0x60 0x6000 PUSH1 00 0 size\n 0x06 0xf3 0xf3 RETURN\n <CODE>\n */\n\n return\n abi.encodePacked(\n hex\"63\",\n uint32(data.length + 1),\n hex\"80_60_0E_60_00_39_60_00_F3\",\n // Prepend 00 to data so contract can't be called\n hex\"00\",\n data\n );\n }\n}\n"
|
|
},
|
|
"contracts/adapters/Types.sol": {
|
|
"content": "// SPDX-License-Identifier: LGPL-3.0-only\npragma solidity >=0.8.17 <0.9.0;\n\nimport \"@gnosis.pm/safe-contracts/contracts/common/Enum.sol\";\n\ninterface IMultiSend {\n function multiSend(bytes memory transactions) external payable;\n}\n\nstruct UnwrappedTransaction {\n Enum.Operation operation;\n address to;\n uint256 value;\n // We wanna deal in calldata slices. We return location, let invoker slice\n uint256 dataLocation;\n uint256 dataSize;\n}\n\ninterface ITransactionUnwrapper {\n function unwrap(\n address to,\n uint256 value,\n bytes calldata data,\n Enum.Operation operation\n ) external view returns (UnwrappedTransaction[] memory result);\n}\n\ninterface ICustomCondition {\n function check(\n address to,\n uint256 value,\n bytes calldata data,\n Enum.Operation operation,\n uint256 location,\n uint256 size,\n bytes12 extra\n ) external view returns (bool success, bytes32 reason);\n}\n"
|
|
},
|
|
"contracts/packers/BufferPacker.sol": {
|
|
"content": "// SPDX-License-Identifier: LGPL-3.0-only\npragma solidity >=0.8.17 <0.9.0;\n\nimport \"../Types.sol\";\n\n/**\n * @title BufferPacker a library that provides packing and unpacking functions\n * for conditions. It allows packing externally provided ConditionsFlat[] into\n * a storage-optimized buffer, and later unpack it into memory.\n * @author Cristóvão Honorato - <[email protected]>\n */\nlibrary BufferPacker {\n // HEADER (stored as a single word in storage)\n // 2 bytes -> count (Condition count)\n // 1 bytes -> options (ExecutionOptions)\n // 1 bytes -> isWildcarded\n // 8 bytes -> unused\n // 20 bytes -> pointer (address containining packed conditions)\n uint256 private constant OFFSET_COUNT = 240;\n uint256 private constant OFFSET_OPTIONS = 224;\n uint256 private constant OFFSET_IS_WILDCARDED = 216;\n uint256 private constant MASK_COUNT = 0xffff << OFFSET_COUNT;\n uint256 private constant MASK_OPTIONS = 0xff << OFFSET_OPTIONS;\n uint256 private constant MASK_IS_WILDCARDED = 0x1 << OFFSET_IS_WILDCARDED;\n // CONDITION (stored as runtimeBytecode at pointer address kept in header)\n // 8 bits -> parent\n // 3 bits -> type\n // 5 bits -> operator\n uint256 private constant BYTES_PER_CONDITION = 2;\n uint16 private constant OFFSET_PARENT = 8;\n uint16 private constant OFFSET_PARAM_TYPE = 5;\n uint16 private constant OFFSET_OPERATOR = 0;\n uint16 private constant MASK_PARENT = uint16(0xff << OFFSET_PARENT);\n uint16 private constant MASK_PARAM_TYPE = uint16(0x07 << OFFSET_PARAM_TYPE);\n uint16 private constant MASK_OPERATOR = uint16(0x1f << OFFSET_OPERATOR);\n\n function packedSize(\n ConditionFlat[] memory conditions\n ) internal pure returns (uint256 result) {\n uint256 count = conditions.length;\n\n result = count * BYTES_PER_CONDITION;\n for (uint256 i; i < count; ++i) {\n if (conditions[i].operator >= Operator.EqualTo) {\n result += 32;\n }\n }\n }\n\n function packHeader(\n uint256 count,\n ExecutionOptions options,\n address pointer\n ) internal pure returns (bytes32) {\n return\n bytes32(count << OFFSET_COUNT) |\n (bytes32(uint256(options)) << OFFSET_OPTIONS) |\n bytes32(uint256(uint160(pointer)));\n }\n\n function packHeaderAsWildcarded(\n ExecutionOptions options\n ) internal pure returns (bytes32) {\n return\n bytes32(uint256(options) << OFFSET_OPTIONS) |\n bytes32(MASK_IS_WILDCARDED);\n }\n\n function unpackHeader(\n bytes32 header\n ) internal pure returns (uint256 count, address pointer) {\n count = (uint256(header) & MASK_COUNT) >> OFFSET_COUNT;\n pointer = address(bytes20(uint160(uint256(header))));\n }\n\n function unpackOptions(\n bytes32 header\n ) internal pure returns (bool isWildcarded, ExecutionOptions options) {\n isWildcarded = uint256(header) & MASK_IS_WILDCARDED != 0;\n options = ExecutionOptions(\n (uint256(header) & MASK_OPTIONS) >> OFFSET_OPTIONS\n );\n }\n\n function packCondition(\n bytes memory buffer,\n uint256 index,\n ConditionFlat memory condition\n ) internal pure {\n uint256 offset = index * BYTES_PER_CONDITION;\n buffer[offset] = bytes1(condition.parent);\n buffer[offset + 1] = bytes1(\n (uint8(condition.paramType) << uint8(OFFSET_PARAM_TYPE)) |\n uint8(condition.operator)\n );\n }\n\n function packCompValue(\n bytes memory buffer,\n uint256 offset,\n ConditionFlat memory condition\n ) internal pure {\n bytes32 word = condition.operator == Operator.EqualTo\n ? keccak256(condition.compValue)\n : bytes32(condition.compValue);\n\n assembly {\n mstore(add(buffer, offset), word)\n }\n }\n\n function unpackBody(\n bytes memory buffer,\n uint256 count\n )\n internal\n pure\n returns (ConditionFlat[] memory result, bytes32[] memory compValues)\n {\n result = new ConditionFlat[](count);\n compValues = new bytes32[](count);\n\n bytes32 word;\n uint256 offset = 32;\n uint256 compValueOffset = 32 + count * BYTES_PER_CONDITION;\n\n for (uint256 i; i < count; ) {\n assembly {\n word := mload(add(buffer, offset))\n }\n offset += BYTES_PER_CONDITION;\n\n uint16 bits = uint16(bytes2(word));\n ConditionFlat memory condition = result[i];\n condition.parent = uint8((bits & MASK_PARENT) >> OFFSET_PARENT);\n condition.paramType = ParameterType(\n (bits & MASK_PARAM_TYPE) >> OFFSET_PARAM_TYPE\n );\n condition.operator = Operator(bits & MASK_OPERATOR);\n\n if (condition.operator >= Operator.EqualTo) {\n assembly {\n word := mload(add(buffer, compValueOffset))\n }\n compValueOffset += 32;\n compValues[i] = word;\n }\n unchecked {\n ++i;\n }\n }\n }\n}\n"
|
|
},
|
|
"contracts/packers/Packer.sol": {
|
|
"content": "// SPDX-License-Identifier: LGPL-3.0-only\npragma solidity >=0.8.17 <0.9.0;\n\nimport \"@gnosis.pm/zodiac/contracts/core/Modifier.sol\";\n\nimport \"./BufferPacker.sol\";\n\n/**\n * @title Packer - a library that coordinates the process of packing\n * conditionsFlat into a storage optimized buffer.\n * @author Cristóvão Honorato - <[email protected]>\n */\nlibrary Packer {\n function pack(\n ConditionFlat[] memory conditionsFlat\n ) external pure returns (bytes memory buffer) {\n _removeExtraneousOffsets(conditionsFlat);\n\n buffer = new bytes(BufferPacker.packedSize(conditionsFlat));\n\n uint256 count = conditionsFlat.length;\n uint256 offset = 32 + count * 2;\n for (uint256 i; i < count; ++i) {\n BufferPacker.packCondition(buffer, i, conditionsFlat[i]);\n if (conditionsFlat[i].operator >= Operator.EqualTo) {\n BufferPacker.packCompValue(buffer, offset, conditionsFlat[i]);\n offset += 32;\n }\n }\n }\n\n /**\n * @dev This function removes unnecessary offsets from compValue fields of\n * the `conditions` array. Its purpose is to ensure a consistent API where\n * every `compValue` provided for use in `Operations.EqualsTo` is obtained\n * by calling `abi.encode` directly.\n *\n * By removing the leading extraneous offsets this function makes\n * abi.encode(...) match the output produced by Decoder inspection.\n * Without it, the encoded fields would need to be patched externally\n * depending on whether the payload is fully encoded inline or not.\n *\n * @param conditionsFlat Array of ConditionFlat structs to remove extraneous\n * offsets from\n */\n function _removeExtraneousOffsets(\n ConditionFlat[] memory conditionsFlat\n ) private pure {\n uint256 count = conditionsFlat.length;\n for (uint256 i; i < count; ++i) {\n if (\n conditionsFlat[i].operator == Operator.EqualTo &&\n !_isInline(conditionsFlat, i)\n ) {\n bytes memory compValue = conditionsFlat[i].compValue;\n uint256 length = compValue.length;\n assembly {\n compValue := add(compValue, 32)\n mstore(compValue, sub(length, 32))\n }\n conditionsFlat[i].compValue = compValue;\n }\n }\n }\n\n function _isInline(\n ConditionFlat[] memory conditions,\n uint256 index\n ) private pure returns (bool) {\n ParameterType paramType = conditions[index].paramType;\n if (paramType == ParameterType.Static) {\n return true;\n } else if (\n paramType == ParameterType.Dynamic ||\n paramType == ParameterType.Array ||\n paramType == ParameterType.Calldata ||\n paramType == ParameterType.AbiEncoded\n ) {\n return false;\n } else {\n uint256 length = conditions.length;\n\n for (uint256 j = index + 1; j < length; ++j) {\n uint8 parent = conditions[j].parent;\n if (parent < index) {\n continue;\n }\n\n if (parent > index) {\n break;\n }\n\n if (!_isInline(conditions, j)) {\n return false;\n }\n }\n return true;\n }\n }\n}\n"
|
|
}
|
|
},
|
|
"settings": {
|
|
"optimizer": {
|
|
"enabled": true,
|
|
"runs": 100
|
|
},
|
|
"outputSelection": {
|
|
"*": {
|
|
"*": [
|
|
"evm.bytecode",
|
|
"evm.deployedBytecode",
|
|
"devdoc",
|
|
"userdoc",
|
|
"metadata",
|
|
"abi"
|
|
]
|
|
}
|
|
},
|
|
"libraries": {
|
|
"contracts/Roles.sol": {
|
|
"Integrity": "0x6a6af4b16458bc39817e4019fb02bd3b26d41049",
|
|
"Packer": "0x869718c939652084bc491fbc5ce0d3c1d5b309f0"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |