From 11517c77a09b4bf96504e5847e654e9599336f89 Mon Sep 17 00:00:00 2001 From: lumoswiz Date: Wed, 5 Feb 2025 14:16:31 +0100 Subject: [PATCH 01/37] ci: update dependency installation in workflow Update CI workflow to install all project dependencies via 'ape pm install' Previously only installed solady dependency --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 999a9d1..40b94e3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -138,7 +138,7 @@ jobs: ape-plugins-list: 'solidity==0.8.5 alchemy==0.8.7 etherscan==0.8.4 foundry==0.8.7' - name: 'Install contract dependencies' - run: 'ape pm install gh:Vectorized/solady --name solady --ref v0.1.3' + run: 'ape pm install' - name: 'Run tests' run: 'ape test -s' From e50a2d8ccfaadfdf96076700239372525ec4edab Mon Sep 17 00:00:00 2001 From: lumoswiz Date: Wed, 5 Feb 2025 14:17:38 +0100 Subject: [PATCH 02/37] feat: add zodiac module dependencies and initial trading module Add Gnosis Guild Zodiac Module dependencies to contracts/dependencies Create initial AgentTradingModule contract Update remappings in ape-config.yaml for new dependencies Temporarily include Zodiac contract dependencies locally to resolve compilation issues --- cow-trader/ape-config.yaml | 4 + cow-trader/contracts/AgentTradingModule.sol | 6 + .../zodiac/contracts/core/Module.sol | 64 +++++ .../contracts/factory/FactoryFriendly.sol | 10 + .../zodiac/contracts/interfaces/IAvatar.sol | 65 +++++ .../safe-contracts/contracts/common/Enum.sol | 11 + .../access/OwnableUpgradeable.sol | 119 +++++++++ .../proxy/utils/Initializable.sol | 228 ++++++++++++++++++ .../utils/ContextUpgradeable.sol | 34 +++ 9 files changed, 541 insertions(+) create mode 100644 cow-trader/contracts/AgentTradingModule.sol create mode 100644 cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/core/Module.sol create mode 100644 cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/factory/FactoryFriendly.sol create mode 100644 cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/interfaces/IAvatar.sol create mode 100644 cow-trader/contracts/dependencies/@gnosis.pm/safe-contracts/contracts/common/Enum.sol create mode 100644 cow-trader/contracts/dependencies/@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol create mode 100644 cow-trader/contracts/dependencies/@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol create mode 100644 cow-trader/contracts/dependencies/@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol diff --git a/cow-trader/ape-config.yaml b/cow-trader/ape-config.yaml index e8a6a9d..bc5a31f 100644 --- a/cow-trader/ape-config.yaml +++ b/cow-trader/ape-config.yaml @@ -12,6 +12,10 @@ plugins: solidity: version: 0.8.25 + import_remapping: + - '@gnosis.pm/safe-contracts=./contracts/dependencies/@gnosis.pm/safe-contracts' + - '@gnosis-guild/zodiac=./contracts/dependencies/@gnosis-guild/zodiac' + - '@openzeppelin/=contracts/dependencies/@openzeppelin/' dependencies: - name: Solady diff --git a/cow-trader/contracts/AgentTradingModule.sol b/cow-trader/contracts/AgentTradingModule.sol new file mode 100644 index 0000000..3d0bb86 --- /dev/null +++ b/cow-trader/contracts/AgentTradingModule.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {Module} from "@gnosis-guild/zodiac/contracts/core/Module.sol"; + +abstract contract AgentTradingModule is Module {} diff --git a/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/core/Module.sol b/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/core/Module.sol new file mode 100644 index 0000000..39ad9e8 --- /dev/null +++ b/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/core/Module.sol @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity >=0.7.0 <0.9.0; + +import {Enum} from "@gnosis.pm/safe-contracts/contracts/common/Enum.sol"; + +import {FactoryFriendly} from "../factory/FactoryFriendly.sol"; +import {IAvatar} from "../interfaces/IAvatar.sol"; + +/// @title Module Interface - A contract that can pass messages to a Module Manager contract if enabled by that contract. +abstract contract Module is FactoryFriendly { + /// @dev Address that will ultimately execute function calls. + address public avatar; + /// @dev Address that this module will pass transactions to. + address public target; + + /// @dev Emitted each time the avatar is set. + event AvatarSet(address indexed previousAvatar, address indexed newAvatar); + /// @dev Emitted each time the Target is set. + event TargetSet(address indexed previousTarget, address indexed newTarget); + + /// @dev Sets the avatar to a new avatar (`newAvatar`). + /// @notice Can only be called by the current owner. + function setAvatar(address _avatar) public onlyOwner { + address previousAvatar = avatar; + avatar = _avatar; + emit AvatarSet(previousAvatar, _avatar); + } + + /// @dev Sets the target to a new target (`newTarget`). + /// @notice Can only be called by the current owner. + function setTarget(address _target) public onlyOwner { + address previousTarget = target; + target = _target; + emit TargetSet(previousTarget, _target); + } + + /// @dev Passes a transaction to be executed by the avatar. + /// @notice Can only be called by this contract. + /// @param to Destination address of module transaction. + /// @param value Ether value of module transaction. + /// @param data Data payload of module transaction. + /// @param operation Operation type of module transaction: 0 == call, 1 == delegate call. + function exec(address to, uint256 value, bytes memory data, Enum.Operation operation) + internal + virtual + returns (bool success) + { + return IAvatar(target).execTransactionFromModule(to, value, data, operation); + } + + /// @dev Passes a transaction to be executed by the target and returns data. + /// @notice Can only be called by this contract. + /// @param to Destination address of module transaction. + /// @param value Ether value of module transaction. + /// @param data Data payload of module transaction. + /// @param operation Operation type of module transaction: 0 == call, 1 == delegate call. + function execAndReturnData(address to, uint256 value, bytes memory data, Enum.Operation operation) + internal + virtual + returns (bool success, bytes memory returnData) + { + return IAvatar(target).execTransactionFromModuleReturnData(to, value, data, operation); + } +} diff --git a/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/factory/FactoryFriendly.sol b/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/factory/FactoryFriendly.sol new file mode 100644 index 0000000..ca4c964 --- /dev/null +++ b/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/factory/FactoryFriendly.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: LGPL-3.0-only + +/// @title Zodiac FactoryFriendly - A contract that allows other contracts to be initializable and pass bytes as arguments to define contract state +pragma solidity >=0.7.0 <0.9.0; + +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; + +abstract contract FactoryFriendly is OwnableUpgradeable { + function setUp(bytes memory initializeParams) public virtual; +} diff --git a/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/interfaces/IAvatar.sol b/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/interfaces/IAvatar.sol new file mode 100644 index 0000000..6ad3f24 --- /dev/null +++ b/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/interfaces/IAvatar.sol @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: LGPL-3.0-only + +/// @title Zodiac Avatar - A contract that manages modules that can execute transactions via this contract. +pragma solidity >=0.7.0 <0.9.0; + +import {Enum} from "@gnosis.pm/safe-contracts/contracts/common/Enum.sol"; + +interface IAvatar { + event EnabledModule(address module); + event DisabledModule(address module); + event ExecutionFromModuleSuccess(address indexed module); + event ExecutionFromModuleFailure(address indexed module); + + /// @dev Enables a module on the avatar. + /// @notice Can only be called by the avatar. + /// @notice Modules should be stored as a linked list. + /// @notice Must emit EnabledModule(address module) if successful. + /// @param module Module to be enabled. + function enableModule(address module) external; + + /// @dev Disables a module on the avatar. + /// @notice Can only be called by the avatar. + /// @notice Must emit DisabledModule(address module) if successful. + /// @param prevModule Address that pointed to the module to be removed in the linked list + /// @param module Module to be removed. + function disableModule(address prevModule, address module) external; + + /// @dev Allows a Module to execute a transaction. + /// @notice Can only be called by an enabled module. + /// @notice Must emit ExecutionFromModuleSuccess(address module) if successful. + /// @notice Must emit ExecutionFromModuleFailure(address module) if unsuccessful. + /// @param to Destination address of module transaction. + /// @param value Ether value of module transaction. + /// @param data Data payload of module transaction. + /// @param operation Operation type of module transaction: 0 == call, 1 == delegate call. + function execTransactionFromModule(address to, uint256 value, bytes memory data, Enum.Operation operation) + external + returns (bool success); + + /// @dev Allows a Module to execute a transaction and return data + /// @notice Can only be called by an enabled module. + /// @notice Must emit ExecutionFromModuleSuccess(address module) if successful. + /// @notice Must emit ExecutionFromModuleFailure(address module) if unsuccessful. + /// @param to Destination address of module transaction. + /// @param value Ether value of module transaction. + /// @param data Data payload of module transaction. + /// @param operation Operation type of module transaction: 0 == call, 1 == delegate call. + function execTransactionFromModuleReturnData(address to, uint256 value, bytes memory data, Enum.Operation operation) + external + returns (bool success, bytes memory returnData); + + /// @dev Returns if an module is enabled + /// @return True if the module is enabled + function isModuleEnabled(address module) external view returns (bool); + + /// @dev Returns array of modules. + /// @param start Start of the page. + /// @param pageSize Maximum number of modules that should be returned. + /// @return array Array of modules. + /// @return next Start of the next page. + function getModulesPaginated(address start, uint256 pageSize) + external + view + returns (address[] memory array, address next); +} diff --git a/cow-trader/contracts/dependencies/@gnosis.pm/safe-contracts/contracts/common/Enum.sol b/cow-trader/contracts/dependencies/@gnosis.pm/safe-contracts/contracts/common/Enum.sol new file mode 100644 index 0000000..4b7c8ca --- /dev/null +++ b/cow-trader/contracts/dependencies/@gnosis.pm/safe-contracts/contracts/common/Enum.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity >=0.7.0 <0.9.0; + +/// @title Enum - Collection of enums +/// @author Richard Meissner - +contract Enum { + enum Operation { + Call, + DelegateCall + } +} diff --git a/cow-trader/contracts/dependencies/@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol b/cow-trader/contracts/dependencies/@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol new file mode 100644 index 0000000..fae3fef --- /dev/null +++ b/cow-trader/contracts/dependencies/@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol) + +pragma solidity ^0.8.20; + +import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol"; +import {Initializable} from "../proxy/utils/Initializable.sol"; + +/** + * @dev Contract module which provides a basic access control mechanism, where + * there is an account (an owner) that can be granted exclusive access to + * specific functions. + * + * The initial owner is set to the address provided by the deployer. This can + * later be changed with {transferOwnership}. + * + * This module is used through inheritance. It will make available the modifier + * `onlyOwner`, which can be applied to your functions to restrict their use to + * the owner. + */ +abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable { + /// @custom:storage-location erc7201:openzeppelin.storage.Ownable + struct OwnableStorage { + address _owner; + } + + // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable")) - 1)) & ~bytes32(uint256(0xff)) + bytes32 private constant OwnableStorageLocation = 0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300; + + function _getOwnableStorage() private pure returns (OwnableStorage storage $) { + assembly { + $.slot := OwnableStorageLocation + } + } + + /** + * @dev The caller account is not authorized to perform an operation. + */ + error OwnableUnauthorizedAccount(address account); + + /** + * @dev The owner is not a valid owner account. (eg. `address(0)`) + */ + error OwnableInvalidOwner(address owner); + + event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); + + /** + * @dev Initializes the contract setting the address provided by the deployer as the initial owner. + */ + function __Ownable_init(address initialOwner) internal onlyInitializing { + __Ownable_init_unchained(initialOwner); + } + + function __Ownable_init_unchained(address initialOwner) internal onlyInitializing { + if (initialOwner == address(0)) { + revert OwnableInvalidOwner(address(0)); + } + _transferOwnership(initialOwner); + } + + /** + * @dev Throws if called by any account other than the owner. + */ + modifier onlyOwner() { + _checkOwner(); + _; + } + + /** + * @dev Returns the address of the current owner. + */ + function owner() public view virtual returns (address) { + OwnableStorage storage $ = _getOwnableStorage(); + return $._owner; + } + + /** + * @dev Throws if the sender is not the owner. + */ + function _checkOwner() internal view virtual { + if (owner() != _msgSender()) { + revert OwnableUnauthorizedAccount(_msgSender()); + } + } + + /** + * @dev Leaves the contract without owner. It will not be possible to call + * `onlyOwner` functions. Can only be called by the current owner. + * + * NOTE: Renouncing ownership will leave the contract without an owner, + * thereby disabling any functionality that is only available to the owner. + */ + function renounceOwnership() public virtual onlyOwner { + _transferOwnership(address(0)); + } + + /** + * @dev Transfers ownership of the contract to a new account (`newOwner`). + * Can only be called by the current owner. + */ + function transferOwnership(address newOwner) public virtual onlyOwner { + if (newOwner == address(0)) { + revert OwnableInvalidOwner(address(0)); + } + _transferOwnership(newOwner); + } + + /** + * @dev Transfers ownership of the contract to a new account (`newOwner`). + * Internal function without access restriction. + */ + function _transferOwnership(address newOwner) internal virtual { + OwnableStorage storage $ = _getOwnableStorage(); + address oldOwner = $._owner; + $._owner = newOwner; + emit OwnershipTransferred(oldOwner, newOwner); + } +} diff --git a/cow-trader/contracts/dependencies/@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol b/cow-trader/contracts/dependencies/@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol new file mode 100644 index 0000000..b3d82b5 --- /dev/null +++ b/cow-trader/contracts/dependencies/@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol @@ -0,0 +1,228 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol) + +pragma solidity ^0.8.20; + +/** + * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed + * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an + * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer + * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. + * + * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be + * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in + * case an upgrade adds a module that needs to be initialized. + * + * For example: + * + * [.hljs-theme-light.nopadding] + * ```solidity + * contract MyToken is ERC20Upgradeable { + * function initialize() initializer public { + * __ERC20_init("MyToken", "MTK"); + * } + * } + * + * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { + * function initializeV2() reinitializer(2) public { + * __ERC20Permit_init("MyToken"); + * } + * } + * ``` + * + * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as + * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. + * + * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure + * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. + * + * [CAUTION] + * ==== + * Avoid leaving a contract uninitialized. + * + * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation + * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke + * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: + * + * [.hljs-theme-light.nopadding] + * ``` + * /// @custom:oz-upgrades-unsafe-allow constructor + * constructor() { + * _disableInitializers(); + * } + * ``` + * ==== + */ +abstract contract Initializable { + /** + * @dev Storage of the initializable contract. + * + * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions + * when using with upgradeable contracts. + * + * @custom:storage-location erc7201:openzeppelin.storage.Initializable + */ + struct InitializableStorage { + /** + * @dev Indicates that the contract has been initialized. + */ + uint64 _initialized; + /** + * @dev Indicates that the contract is in the process of being initialized. + */ + bool _initializing; + } + + // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff)) + bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00; + + /** + * @dev The contract is already initialized. + */ + error InvalidInitialization(); + + /** + * @dev The contract is not initializing. + */ + error NotInitializing(); + + /** + * @dev Triggered when the contract has been initialized or reinitialized. + */ + event Initialized(uint64 version); + + /** + * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, + * `onlyInitializing` functions can be used to initialize parent contracts. + * + * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any + * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in + * production. + * + * Emits an {Initialized} event. + */ + modifier initializer() { + // solhint-disable-next-line var-name-mixedcase + InitializableStorage storage $ = _getInitializableStorage(); + + // Cache values to avoid duplicated sloads + bool isTopLevelCall = !$._initializing; + uint64 initialized = $._initialized; + + // Allowed calls: + // - initialSetup: the contract is not in the initializing state and no previous version was + // initialized + // - construction: the contract is initialized at version 1 (no reininitialization) and the + // current contract is just being deployed + bool initialSetup = initialized == 0 && isTopLevelCall; + bool construction = initialized == 1 && address(this).code.length == 0; + + if (!initialSetup && !construction) { + revert InvalidInitialization(); + } + $._initialized = 1; + if (isTopLevelCall) { + $._initializing = true; + } + _; + if (isTopLevelCall) { + $._initializing = false; + emit Initialized(1); + } + } + + /** + * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the + * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be + * used to initialize parent contracts. + * + * A reinitializer may be used after the original initialization step. This is essential to configure modules that + * are added through upgrades and that require initialization. + * + * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer` + * cannot be nested. If one is invoked in the context of another, execution will revert. + * + * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in + * a contract, executing them in the right order is up to the developer or operator. + * + * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization. + * + * Emits an {Initialized} event. + */ + modifier reinitializer(uint64 version) { + // solhint-disable-next-line var-name-mixedcase + InitializableStorage storage $ = _getInitializableStorage(); + + if ($._initializing || $._initialized >= version) { + revert InvalidInitialization(); + } + $._initialized = version; + $._initializing = true; + _; + $._initializing = false; + emit Initialized(version); + } + + /** + * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the + * {initializer} and {reinitializer} modifiers, directly or indirectly. + */ + modifier onlyInitializing() { + _checkInitializing(); + _; + } + + /** + * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}. + */ + function _checkInitializing() internal view virtual { + if (!_isInitializing()) { + revert NotInitializing(); + } + } + + /** + * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. + * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized + * to any version. It is recommended to use this to lock implementation contracts that are designed to be called + * through proxies. + * + * Emits an {Initialized} event the first time it is successfully executed. + */ + function _disableInitializers() internal virtual { + // solhint-disable-next-line var-name-mixedcase + InitializableStorage storage $ = _getInitializableStorage(); + + if ($._initializing) { + revert InvalidInitialization(); + } + if ($._initialized != type(uint64).max) { + $._initialized = type(uint64).max; + emit Initialized(type(uint64).max); + } + } + + /** + * @dev Returns the highest version that has been initialized. See {reinitializer}. + */ + function _getInitializedVersion() internal view returns (uint64) { + return _getInitializableStorage()._initialized; + } + + /** + * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}. + */ + function _isInitializing() internal view returns (bool) { + return _getInitializableStorage()._initializing; + } + + /** + * @dev Returns a pointer to the storage namespace. + */ + // solhint-disable-next-line var-name-mixedcase + function _getInitializableStorage() private pure returns (InitializableStorage storage $) { + assembly { + $.slot := INITIALIZABLE_STORAGE + } + } +} diff --git a/cow-trader/contracts/dependencies/@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol b/cow-trader/contracts/dependencies/@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol new file mode 100644 index 0000000..0d1d4f5 --- /dev/null +++ b/cow-trader/contracts/dependencies/@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol) + +pragma solidity ^0.8.20; + +import {Initializable} from "../proxy/utils/Initializable.sol"; + +/** + * @dev Provides information about the current execution context, including the + * sender of the transaction and its data. While these are generally available + * via msg.sender and msg.data, they should not be accessed in such a direct + * manner, since when dealing with meta-transactions the account sending and + * paying for execution may not be the actual sender (as far as an application + * is concerned). + * + * This contract is only required for intermediate, library-like contracts. + */ +abstract contract ContextUpgradeable is Initializable { + function __Context_init() internal onlyInitializing {} + + function __Context_init_unchained() internal onlyInitializing {} + + function _msgSender() internal view virtual returns (address) { + return msg.sender; + } + + function _msgData() internal view virtual returns (bytes calldata) { + return msg.data; + } + + function _contextSuffixLength() internal view virtual returns (uint256) { + return 0; + } +} From b3354e1871bb19669c7f333fa6f3f3b62a97bc6d Mon Sep 17 00:00:00 2001 From: lumoswiz Date: Wed, 5 Feb 2025 15:01:22 +0100 Subject: [PATCH 03/37] fix: use explicit import paths for Zodiac dependencies Replace remapping-based imports with explicit relative paths Add missing Zodiac guard contracts and interfaces Add OpenZeppelin contracts for IERC165 Update ape-config.yaml remapping configuration --- cow-trader/ape-config.yaml | 4 - cow-trader/contracts/AgentTradingModule.sol | 81 ++++++++++++++++++- .../zodiac/contracts/core/Module.sol | 2 +- .../contracts/factory/FactoryFriendly.sol | 2 +- .../zodiac/contracts/guard/BaseGuard.sol | 33 ++++++++ .../zodiac/contracts/guard/Guardable.sol | 33 ++++++++ .../zodiac/contracts/interfaces/IAvatar.sol | 2 +- .../zodiac/contracts/interfaces/IGuard.sol | 22 +++++ .../contracts/utils/introspection/IERC165.sol | 25 ++++++ 9 files changed, 195 insertions(+), 9 deletions(-) create mode 100644 cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/guard/BaseGuard.sol create mode 100644 cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/guard/Guardable.sol create mode 100644 cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/interfaces/IGuard.sol create mode 100644 cow-trader/contracts/dependencies/@openzeppelin/contracts/utils/introspection/IERC165.sol diff --git a/cow-trader/ape-config.yaml b/cow-trader/ape-config.yaml index bc5a31f..e8a6a9d 100644 --- a/cow-trader/ape-config.yaml +++ b/cow-trader/ape-config.yaml @@ -12,10 +12,6 @@ plugins: solidity: version: 0.8.25 - import_remapping: - - '@gnosis.pm/safe-contracts=./contracts/dependencies/@gnosis.pm/safe-contracts' - - '@gnosis-guild/zodiac=./contracts/dependencies/@gnosis-guild/zodiac' - - '@openzeppelin/=contracts/dependencies/@openzeppelin/' dependencies: - name: Solady diff --git a/cow-trader/contracts/AgentTradingModule.sol b/cow-trader/contracts/AgentTradingModule.sol index 3d0bb86..759c482 100644 --- a/cow-trader/contracts/AgentTradingModule.sol +++ b/cow-trader/contracts/AgentTradingModule.sol @@ -1,6 +1,83 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.25; -import {Module} from "@gnosis-guild/zodiac/contracts/core/Module.sol"; +import {Enum} from "./dependencies/@gnosis.pm/safe-contracts/contracts/common/Enum.sol"; +import {Module} from "./dependencies/@gnosis-guild/zodiac/contracts/core/Module.sol"; +import {IGuard} from "./dependencies/@gnosis-guild/zodiac/contracts/interfaces/IGuard.sol"; +import {Guardable} from "./dependencies/@gnosis-guild/zodiac/contracts/guard/Guardable.sol"; +import {IAvatar} from "./dependencies/@gnosis-guild/zodiac/contracts/interfaces/IAvatar.sol"; -abstract contract AgentTradingModule is Module {} +abstract contract AgentTradingModule is Module, Guardable { + /// @dev Passes a transaction to be executed by the avatar. + /// @notice Can only be called by this contract. + /// @param to Destination address of module transaction. + /// @param value Ether value of module transaction. + /// @param data Data payload of module transaction. + /// @param operation Operation type of module transaction: 0 == call, 1 == delegate call. + function exec(address to, uint256 value, bytes memory data, Enum.Operation operation) + internal + override + returns (bool success) + { + address currentGuard = guard; + if (currentGuard != address(0)) { + IGuard(currentGuard).checkTransaction( + /// Transaction info used by module transactions. + to, + value, + data, + operation, + /// Zero out the redundant transaction information only used for Safe multisig transctions. + 0, + 0, + 0, + address(0), + payable(0), + "", + msg.sender + ); + } + success = IAvatar(target).execTransactionFromModule(to, value, data, operation); + if (currentGuard != address(0)) { + IGuard(currentGuard).checkAfterExecution(bytes32(0), success); + } + } + + /// @dev Passes a transaction to be executed by the target and returns data. + /// @notice Can only be called by this contract. + /// @param to Destination address of module transaction. + /// @param value Ether value of module transaction. + /// @param data Data payload of module transaction. + /// @param operation Operation type of module transaction: 0 == call, 1 == delegate call. + function execAndReturnData(address to, uint256 value, bytes memory data, Enum.Operation operation) + internal + virtual + override + returns (bool success, bytes memory returnData) + { + address currentGuard = guard; + if (currentGuard != address(0)) { + IGuard(currentGuard).checkTransaction( + /// Transaction info used by module transactions. + to, + value, + data, + operation, + /// Zero out the redundant transaction information only used for Safe multisig transctions. + 0, + 0, + 0, + address(0), + payable(0), + "", + msg.sender + ); + } + + (success, returnData) = IAvatar(target).execTransactionFromModuleReturnData(to, value, data, operation); + + if (currentGuard != address(0)) { + IGuard(currentGuard).checkAfterExecution(bytes32(0), success); + } + } +} diff --git a/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/core/Module.sol b/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/core/Module.sol index 39ad9e8..509549f 100644 --- a/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/core/Module.sol +++ b/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/core/Module.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity >=0.7.0 <0.9.0; -import {Enum} from "@gnosis.pm/safe-contracts/contracts/common/Enum.sol"; +import {Enum} from "../../../../@gnosis.pm/safe-contracts/contracts/common/Enum.sol"; import {FactoryFriendly} from "../factory/FactoryFriendly.sol"; import {IAvatar} from "../interfaces/IAvatar.sol"; diff --git a/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/factory/FactoryFriendly.sol b/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/factory/FactoryFriendly.sol index ca4c964..491509b 100644 --- a/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/factory/FactoryFriendly.sol +++ b/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/factory/FactoryFriendly.sol @@ -3,7 +3,7 @@ /// @title Zodiac FactoryFriendly - A contract that allows other contracts to be initializable and pass bytes as arguments to define contract state pragma solidity >=0.7.0 <0.9.0; -import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import {OwnableUpgradeable} from "../../../../@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; abstract contract FactoryFriendly is OwnableUpgradeable { function setUp(bytes memory initializeParams) public virtual; diff --git a/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/guard/BaseGuard.sol b/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/guard/BaseGuard.sol new file mode 100644 index 0000000..11dbc97 --- /dev/null +++ b/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/guard/BaseGuard.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity >=0.7.0 <0.9.0; + +import {Enum} from "../../../../@gnosis.pm/safe-contracts/contracts/common/Enum.sol"; +import {IERC165} from "../../../../@openzeppelin/contracts/utils/introspection/IERC165.sol"; + +import {IGuard} from "../interfaces/IGuard.sol"; + +abstract contract BaseGuard is IERC165 { + function supportsInterface(bytes4 interfaceId) external pure override returns (bool) { + return interfaceId == type(IGuard).interfaceId // 0xe6d7a83a + || interfaceId == type(IERC165).interfaceId; // 0x01ffc9a7 + } + + /// @dev Module transactions only use the first four parameters: to, value, data, and operation. + /// Module.sol hardcodes the remaining parameters as 0 since they are not used for module transactions. + /// @notice This interface is used to maintain compatibilty with Gnosis Safe transaction guards. + function checkTransaction( + address to, + uint256 value, + bytes memory data, + Enum.Operation operation, + uint256 safeTxGas, + uint256 baseGas, + uint256 gasPrice, + address gasToken, + address payable refundReceiver, + bytes memory signatures, + address msgSender + ) external virtual; + + function checkAfterExecution(bytes32 txHash, bool success) external virtual; +} diff --git a/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/guard/Guardable.sol b/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/guard/Guardable.sol new file mode 100644 index 0000000..fc1f18e --- /dev/null +++ b/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/guard/Guardable.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity >=0.7.0 <0.9.0; + +import {OwnableUpgradeable} from "../../../../@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; + +import {BaseGuard} from "../guard/BaseGuard.sol"; +import {IGuard} from "../interfaces/IGuard.sol"; + +/// @title Guardable - A contract that manages fallback calls made to this contract +contract Guardable is OwnableUpgradeable { + address public guard; + + event ChangedGuard(address guard); + + /// `guard_` does not implement IERC165. + error NotIERC165Compliant(address guard_); + + /// @dev Set a guard that checks transactions before execution. + /// @param _guard The address of the guard to be used or the 0 address to disable the guard. + function setGuard(address _guard) external onlyOwner { + if (_guard != address(0)) { + if (!BaseGuard(_guard).supportsInterface(type(IGuard).interfaceId)) { + revert NotIERC165Compliant(_guard); + } + } + guard = _guard; + emit ChangedGuard(guard); + } + + function getGuard() external view returns (address _guard) { + return guard; + } +} diff --git a/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/interfaces/IAvatar.sol b/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/interfaces/IAvatar.sol index 6ad3f24..bcfe7bd 100644 --- a/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/interfaces/IAvatar.sol +++ b/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/interfaces/IAvatar.sol @@ -3,7 +3,7 @@ /// @title Zodiac Avatar - A contract that manages modules that can execute transactions via this contract. pragma solidity >=0.7.0 <0.9.0; -import {Enum} from "@gnosis.pm/safe-contracts/contracts/common/Enum.sol"; +import {Enum} from "../../../../@gnosis.pm/safe-contracts/contracts/common/Enum.sol"; interface IAvatar { event EnabledModule(address module); diff --git a/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/interfaces/IGuard.sol b/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/interfaces/IGuard.sol new file mode 100644 index 0000000..2b96428 --- /dev/null +++ b/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/interfaces/IGuard.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity >=0.7.0 <0.9.0; + +import {Enum} from "../../../../@gnosis.pm/safe-contracts/contracts/common/Enum.sol"; + +interface IGuard { + function checkTransaction( + address to, + uint256 value, + bytes memory data, + Enum.Operation operation, + uint256 safeTxGas, + uint256 baseGas, + uint256 gasPrice, + address gasToken, + address payable refundReceiver, + bytes memory signatures, + address msgSender + ) external; + + function checkAfterExecution(bytes32 txHash, bool success) external; +} diff --git a/cow-trader/contracts/dependencies/@openzeppelin/contracts/utils/introspection/IERC165.sol b/cow-trader/contracts/dependencies/@openzeppelin/contracts/utils/introspection/IERC165.sol new file mode 100644 index 0000000..719ec35 --- /dev/null +++ b/cow-trader/contracts/dependencies/@openzeppelin/contracts/utils/introspection/IERC165.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol) + +pragma solidity ^0.8.20; + +/** + * @dev Interface of the ERC-165 standard, as defined in the + * https://eips.ethereum.org/EIPS/eip-165[ERC]. + * + * Implementers can declare support of contract interfaces, which can then be + * queried by others ({ERC165Checker}). + * + * For an implementation, see {ERC165}. + */ +interface IERC165 { + /** + * @dev Returns true if this contract implements the interface defined by + * `interfaceId`. See the corresponding + * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section] + * to learn more about how these ids are created. + * + * This function call must use less than 30 000 gas. + */ + function supportsInterface(bytes4 interfaceId) external view returns (bool); +} From 896f1668455a340ad90ac0836670c6e7820a8add Mon Sep 17 00:00:00 2001 From: lumoswiz Date: Wed, 5 Feb 2025 22:50:52 +0100 Subject: [PATCH 04/37] refactor: remove guardable --- cow-trader/contracts/AgentTradingModule.sol | 60 +-------------------- 1 file changed, 1 insertion(+), 59 deletions(-) diff --git a/cow-trader/contracts/AgentTradingModule.sol b/cow-trader/contracts/AgentTradingModule.sol index 759c482..f92eab2 100644 --- a/cow-trader/contracts/AgentTradingModule.sol +++ b/cow-trader/contracts/AgentTradingModule.sol @@ -3,81 +3,23 @@ pragma solidity 0.8.25; import {Enum} from "./dependencies/@gnosis.pm/safe-contracts/contracts/common/Enum.sol"; import {Module} from "./dependencies/@gnosis-guild/zodiac/contracts/core/Module.sol"; -import {IGuard} from "./dependencies/@gnosis-guild/zodiac/contracts/interfaces/IGuard.sol"; -import {Guardable} from "./dependencies/@gnosis-guild/zodiac/contracts/guard/Guardable.sol"; import {IAvatar} from "./dependencies/@gnosis-guild/zodiac/contracts/interfaces/IAvatar.sol"; -abstract contract AgentTradingModule is Module, Guardable { - /// @dev Passes a transaction to be executed by the avatar. - /// @notice Can only be called by this contract. - /// @param to Destination address of module transaction. - /// @param value Ether value of module transaction. - /// @param data Data payload of module transaction. - /// @param operation Operation type of module transaction: 0 == call, 1 == delegate call. +abstract contract AgentTradingModule is Module { function exec(address to, uint256 value, bytes memory data, Enum.Operation operation) internal override returns (bool success) { - address currentGuard = guard; - if (currentGuard != address(0)) { - IGuard(currentGuard).checkTransaction( - /// Transaction info used by module transactions. - to, - value, - data, - operation, - /// Zero out the redundant transaction information only used for Safe multisig transctions. - 0, - 0, - 0, - address(0), - payable(0), - "", - msg.sender - ); - } success = IAvatar(target).execTransactionFromModule(to, value, data, operation); - if (currentGuard != address(0)) { - IGuard(currentGuard).checkAfterExecution(bytes32(0), success); - } } - /// @dev Passes a transaction to be executed by the target and returns data. - /// @notice Can only be called by this contract. - /// @param to Destination address of module transaction. - /// @param value Ether value of module transaction. - /// @param data Data payload of module transaction. - /// @param operation Operation type of module transaction: 0 == call, 1 == delegate call. function execAndReturnData(address to, uint256 value, bytes memory data, Enum.Operation operation) internal virtual override returns (bool success, bytes memory returnData) { - address currentGuard = guard; - if (currentGuard != address(0)) { - IGuard(currentGuard).checkTransaction( - /// Transaction info used by module transactions. - to, - value, - data, - operation, - /// Zero out the redundant transaction information only used for Safe multisig transctions. - 0, - 0, - 0, - address(0), - payable(0), - "", - msg.sender - ); - } - (success, returnData) = IAvatar(target).execTransactionFromModuleReturnData(to, value, data, operation); - - if (currentGuard != address(0)) { - IGuard(currentGuard).checkAfterExecution(bytes32(0), success); - } } } From 112d76e514b822137447464d8457017f89b58c28 Mon Sep 17 00:00:00 2001 From: lumoswiz Date: Wed, 5 Feb 2025 23:38:23 +0100 Subject: [PATCH 05/37] feat: add isOrderAllowed view function --- cow-trader/contracts/TokenAllowlist.sol | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/cow-trader/contracts/TokenAllowlist.sol b/cow-trader/contracts/TokenAllowlist.sol index 03e9149..18ade93 100644 --- a/cow-trader/contracts/TokenAllowlist.sol +++ b/cow-trader/contracts/TokenAllowlist.sol @@ -7,37 +7,41 @@ import {EnumerableSetLib} from "solady/src/utils/EnumerableSetLib.sol"; contract TokenAllowlist is Ownable { using EnumerableSetLib for EnumerableSetLib.AddressSet; - EnumerableSetLib.AddressSet internal allowList; + EnumerableSetLib.AddressSet internal allowlist; constructor() { _initializeOwner(msg.sender); } function addToken(address token) external onlyOwner { - allowList.add(token); + allowlist.add(token); } function addTokensBatch(address[] memory tokens) external onlyOwner { for (uint256 i; i < tokens.length; ++i) { - allowList.add(tokens[i]); + allowlist.add(tokens[i]); } } function removeToken(address token) external onlyOwner { - allowList.remove(token); + allowlist.remove(token); } function removeTokensBatch(address[] memory tokens) external onlyOwner { for (uint256 i; i < tokens.length; ++i) { - allowList.remove(tokens[i]); + allowlist.remove(tokens[i]); } } function allowedTokens() external view returns (address[] memory) { - return allowList.values(); + return allowlist.values(); } - function isAllowed(address token) external view returns (bool) { - return allowList.contains(token); + function isAllowed(address token) public view returns (bool) { + return allowlist.contains(token); + } + + function isOrderAllowed(address sellToken, address buyToken) external view returns (bool) { + return isAllowed(sellToken) && isAllowed(buyToken); } } From 687c2f7aa8163c301bf9af5fcc5864eacbd40df4 Mon Sep 17 00:00:00 2001 From: lumoswiz Date: Wed, 5 Feb 2025 23:38:50 +0100 Subject: [PATCH 06/37] feat: interface for TokenAllowlist --- cow-trader/contracts/interfaces/ITokenAllowlist.sol | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 cow-trader/contracts/interfaces/ITokenAllowlist.sol diff --git a/cow-trader/contracts/interfaces/ITokenAllowlist.sol b/cow-trader/contracts/interfaces/ITokenAllowlist.sol new file mode 100644 index 0000000..5a08fd1 --- /dev/null +++ b/cow-trader/contracts/interfaces/ITokenAllowlist.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +interface ITokenAllowlist { + function isOrderAllowed(address sellToken, address buyToken) external view returns (bool); +} From d52a50f1ba4bf0bb8fd65a2a5fab153a9fb8d772 Mon Sep 17 00:00:00 2001 From: lumoswiz Date: Wed, 5 Feb 2025 23:39:28 +0100 Subject: [PATCH 07/37] chore: add cowprotocol contract dependencies --- .../src/contracts/interfaces/IERC20.sol | 98 ++++++++ .../src/contracts/libraries/GPv2Order.sol | 227 ++++++++++++++++++ 2 files changed, 325 insertions(+) create mode 100644 cow-trader/contracts/dependencies/cowprotocol/contracts/src/contracts/interfaces/IERC20.sol create mode 100644 cow-trader/contracts/dependencies/cowprotocol/contracts/src/contracts/libraries/GPv2Order.sol diff --git a/cow-trader/contracts/dependencies/cowprotocol/contracts/src/contracts/interfaces/IERC20.sol b/cow-trader/contracts/dependencies/cowprotocol/contracts/src/contracts/interfaces/IERC20.sol new file mode 100644 index 0000000..610e9fd --- /dev/null +++ b/cow-trader/contracts/dependencies/cowprotocol/contracts/src/contracts/interfaces/IERC20.sol @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: MIT + +// Vendored from OpenZeppelin contracts with minor modifications: +// - Modified Solidity version +// - Formatted code +// - Added `name`, `symbol` and `decimals` function declarations +// + +pragma solidity >=0.7.6 <0.9.0; + +/** + * @dev Interface of the ERC20 standard as defined in the EIP. + */ +interface IERC20 { + /** + * @dev Returns the name of the token. + */ + function name() external view returns (string memory); + + /** + * @dev Returns the symbol of the token. + */ + function symbol() external view returns (string memory); + + /** + * @dev Returns the number of decimals the token uses. + */ + function decimals() external view returns (uint8); + + /** + * @dev Returns the amount of tokens in existence. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns the amount of tokens owned by `account`. + */ + function balanceOf(address account) external view returns (uint256); + + /** + * @dev Moves `amount` tokens from the caller's account to `recipient`. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transfer(address recipient, uint256 amount) external returns (bool); + + /** + * @dev Returns the remaining number of tokens that `spender` will be + * allowed to spend on behalf of `owner` through {transferFrom}. This is + * zero by default. + * + * This value changes when {approve} or {transferFrom} are called. + */ + function allowance(address owner, address spender) external view returns (uint256); + + /** + * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * IMPORTANT: Beware that changing an allowance with this method brings the risk + * that someone may use both the old and the new allowance by unfortunate + * transaction ordering. One possible solution to mitigate this race + * condition is to first reduce the spender's allowance to 0 and set the + * desired value afterwards: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * + * Emits an {Approval} event. + */ + function approve(address spender, uint256 amount) external returns (bool); + + /** + * @dev Moves `amount` tokens from `sender` to `recipient` using the + * allowance mechanism. `amount` is then deducted from the caller's + * allowance. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); + + /** + * @dev Emitted when `value` tokens are moved from one account (`from`) to + * another (`to`). + * + * Note that `value` may be zero. + */ + event Transfer(address indexed from, address indexed to, uint256 value); + + /** + * @dev Emitted when the allowance of a `spender` for an `owner` is set by + * a call to {approve}. `value` is the new allowance. + */ + event Approval(address indexed owner, address indexed spender, uint256 value); +} diff --git a/cow-trader/contracts/dependencies/cowprotocol/contracts/src/contracts/libraries/GPv2Order.sol b/cow-trader/contracts/dependencies/cowprotocol/contracts/src/contracts/libraries/GPv2Order.sol new file mode 100644 index 0000000..a5fdcf5 --- /dev/null +++ b/cow-trader/contracts/dependencies/cowprotocol/contracts/src/contracts/libraries/GPv2Order.sol @@ -0,0 +1,227 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +pragma solidity >=0.7.6 <0.9.0; + +import "../interfaces/IERC20.sol"; + +/// @title Gnosis Protocol v2 Order Library +/// @author Gnosis Developers +library GPv2Order { + /// @dev The complete data for a Gnosis Protocol order. This struct contains + /// all order parameters that are signed for submitting to GP. + struct Data { + IERC20 sellToken; + IERC20 buyToken; + address receiver; + uint256 sellAmount; + uint256 buyAmount; + uint32 validTo; + bytes32 appData; + uint256 feeAmount; + bytes32 kind; + bool partiallyFillable; + bytes32 sellTokenBalance; + bytes32 buyTokenBalance; + } + + /// @dev The order EIP-712 type hash for the [`GPv2Order.Data`] struct. + /// + /// This value is pre-computed from the following expression: + /// ``` + /// keccak256( + /// "Order(" + + /// "address sellToken," + + /// "address buyToken," + + /// "address receiver," + + /// "uint256 sellAmount," + + /// "uint256 buyAmount," + + /// "uint32 validTo," + + /// "bytes32 appData," + + /// "uint256 feeAmount," + + /// "string kind," + + /// "bool partiallyFillable," + + /// "string sellTokenBalance," + + /// "string buyTokenBalance" + + /// ")" + /// ) + /// ``` + bytes32 internal constant TYPE_HASH = hex"d5a25ba2e97094ad7d83dc28a6572da797d6b3e7fc6663bd93efb789fc17e489"; + + /// @dev The marker value for a sell order for computing the order struct + /// hash. This allows the EIP-712 compatible wallets to display a + /// descriptive string for the order kind (instead of 0 or 1). + /// + /// This value is pre-computed from the following expression: + /// ``` + /// keccak256("sell") + /// ``` + bytes32 internal constant KIND_SELL = hex"f3b277728b3fee749481eb3e0b3b48980dbbab78658fc419025cb16eee346775"; + + /// @dev The OrderKind marker value for a buy order for computing the order + /// struct hash. + /// + /// This value is pre-computed from the following expression: + /// ``` + /// keccak256("buy") + /// ``` + bytes32 internal constant KIND_BUY = hex"6ed88e868af0a1983e3886d5f3e95a2fafbd6c3450bc229e27342283dc429ccc"; + + /// @dev The TokenBalance marker value for using direct ERC20 balances for + /// computing the order struct hash. + /// + /// This value is pre-computed from the following expression: + /// ``` + /// keccak256("erc20") + /// ``` + bytes32 internal constant BALANCE_ERC20 = hex"5a28e9363bb942b639270062aa6bb295f434bcdfc42c97267bf003f272060dc9"; + + /// @dev The TokenBalance marker value for using Balancer Vault external + /// balances (in order to re-use Vault ERC20 approvals) for computing the + /// order struct hash. + /// + /// This value is pre-computed from the following expression: + /// ``` + /// keccak256("external") + /// ``` + bytes32 internal constant BALANCE_EXTERNAL = hex"abee3b73373acd583a130924aad6dc38cfdc44ba0555ba94ce2ff63980ea0632"; + + /// @dev The TokenBalance marker value for using Balancer Vault internal + /// balances for computing the order struct hash. + /// + /// This value is pre-computed from the following expression: + /// ``` + /// keccak256("internal") + /// ``` + bytes32 internal constant BALANCE_INTERNAL = hex"4ac99ace14ee0a5ef932dc609df0943ab7ac16b7583634612f8dc35a4289a6ce"; + + /// @dev Marker address used to indicate that the receiver of the trade + /// proceeds should the owner of the order. + /// + /// This is chosen to be `address(0)` for gas efficiency as it is expected + /// to be the most common case. + address internal constant RECEIVER_SAME_AS_OWNER = address(0); + + /// @dev The byte length of an order unique identifier. + uint256 internal constant UID_LENGTH = 56; + + /// @dev Returns the actual receiver for an order. This function checks + /// whether or not the [`receiver`] field uses the marker value to indicate + /// it is the same as the order owner. + /// + /// @return receiver The actual receiver of trade proceeds. + function actualReceiver(Data memory order, address owner) internal pure returns (address receiver) { + if (order.receiver == RECEIVER_SAME_AS_OWNER) { + receiver = owner; + } else { + receiver = order.receiver; + } + } + + /// @dev Return the EIP-712 signing hash for the specified order. + /// + /// @param order The order to compute the EIP-712 signing hash for. + /// @param domainSeparator The EIP-712 domain separator to use. + /// @return orderDigest The 32 byte EIP-712 struct hash. + function hash(Data memory order, bytes32 domainSeparator) internal pure returns (bytes32 orderDigest) { + bytes32 structHash; + + // NOTE: Compute the EIP-712 order struct hash in place. As suggested + // in the EIP proposal, noting that the order struct has 12 fields, and + // prefixing the type hash `(1 + 12) * 32 = 416` bytes to hash. + // + // solhint-disable-next-line no-inline-assembly + assembly { + let dataStart := sub(order, 32) + let temp := mload(dataStart) + mstore(dataStart, TYPE_HASH) + structHash := keccak256(dataStart, 416) + mstore(dataStart, temp) + } + + // NOTE: Now that we have the struct hash, compute the EIP-712 signing + // hash using scratch memory past the free memory pointer. The signing + // hash is computed from `"\x19\x01" || domainSeparator || structHash`. + // + // + // solhint-disable-next-line no-inline-assembly + assembly { + let freeMemoryPointer := mload(0x40) + mstore(freeMemoryPointer, "\x19\x01") + mstore(add(freeMemoryPointer, 2), domainSeparator) + mstore(add(freeMemoryPointer, 34), structHash) + orderDigest := keccak256(freeMemoryPointer, 66) + } + } + + /// @dev Packs order UID parameters into the specified memory location. The + /// result is equivalent to `abi.encodePacked(...)` with the difference that + /// it allows re-using the memory for packing the order UID. + /// + /// This function reverts if the order UID buffer is not the correct size. + /// + /// @param orderUid The buffer pack the order UID parameters into. + /// @param orderDigest The EIP-712 struct digest derived from the order + /// parameters. + /// @param owner The address of the user who owns this order. + /// @param validTo The epoch time at which the order will stop being valid. + function packOrderUidParams(bytes memory orderUid, bytes32 orderDigest, address owner, uint32 validTo) + internal + pure + { + require(orderUid.length == UID_LENGTH, "GPv2: uid buffer overflow"); + + // NOTE: Write the order UID to the allocated memory buffer. The order + // parameters are written to memory in **reverse order** as memory + // operations write 32-bytes at a time and we want to use a packed + // encoding. This means, for example, that after writing the value of + // `owner` to bytes `20:52`, writing the `orderDigest` to bytes `0:32` + // will **overwrite** bytes `20:32`. This is desirable as addresses are + // only 20 bytes and `20:32` should be `0`s: + // + // | 1111111111222222222233333333334444444444555555 + // byte | 01234567890123456789012345678901234567890123456789012345 + // -------+--------------------------------------------------------- + // field | [.........orderDigest..........][......owner.......][vT] + // -------+--------------------------------------------------------- + // mstore | [000000000000000000000000000.vT] + // | [00000000000.......owner.......] + // | [.........orderDigest..........] + // + // Additionally, since Solidity `bytes memory` are length prefixed, + // 32 needs to be added to all the offsets. + // + // solhint-disable-next-line no-inline-assembly + assembly { + mstore(add(orderUid, 56), validTo) + mstore(add(orderUid, 52), owner) + mstore(add(orderUid, 32), orderDigest) + } + } + + /// @dev Extracts specific order information from the standardized unique + /// order id of the protocol. + /// + /// @param orderUid The unique identifier used to represent an order in + /// the protocol. This uid is the packed concatenation of the order digest, + /// the validTo order parameter and the address of the user who created the + /// order. It is used by the user to interface with the contract directly, + /// and not by calls that are triggered by the solvers. + /// @return orderDigest The EIP-712 signing digest derived from the order + /// parameters. + /// @return owner The address of the user who owns this order. + /// @return validTo The epoch time at which the order will stop being valid. + function extractOrderUidParams(bytes calldata orderUid) + internal + pure + returns (bytes32 orderDigest, address owner, uint32 validTo) + { + require(orderUid.length == UID_LENGTH, "GPv2: invalid uid"); + + // Use assembly to efficiently decode packed calldata. + // solhint-disable-next-line no-inline-assembly + assembly { + orderDigest := calldataload(orderUid.offset) + owner := shr(96, calldataload(add(orderUid.offset, 32))) + validTo := shr(224, calldataload(add(orderUid.offset, 52))) + } + } +} From 425f6bc575c34dbd626ae621fe391512eee1cec4 Mon Sep 17 00:00:00 2001 From: lumoswiz Date: Thu, 6 Feb 2025 00:42:09 +0100 Subject: [PATCH 08/37] feat: implement AgentTradingModule with initial order validation Add constructor and setUp initializer with required dependencies Implement initial setOrderTradeable function with core validations Remove abstract template in favor of concrete implementation --- cow-trader/contracts/AgentTradingModule.sol | 71 +++++++++++++++++---- 1 file changed, 57 insertions(+), 14 deletions(-) diff --git a/cow-trader/contracts/AgentTradingModule.sol b/cow-trader/contracts/AgentTradingModule.sol index f92eab2..672d0db 100644 --- a/cow-trader/contracts/AgentTradingModule.sol +++ b/cow-trader/contracts/AgentTradingModule.sol @@ -4,22 +4,65 @@ pragma solidity 0.8.25; import {Enum} from "./dependencies/@gnosis.pm/safe-contracts/contracts/common/Enum.sol"; import {Module} from "./dependencies/@gnosis-guild/zodiac/contracts/core/Module.sol"; import {IAvatar} from "./dependencies/@gnosis-guild/zodiac/contracts/interfaces/IAvatar.sol"; +import {GPv2Order} from "./dependencies/cowprotocol/contracts/src/contracts/libraries/GPv2Order.sol"; +import {ITokenAllowlist} from "./interfaces/ITokenAllowlist.sol"; -abstract contract AgentTradingModule is Module { - function exec(address to, uint256 value, bytes memory data, Enum.Operation operation) - internal - override - returns (bool success) - { - success = IAvatar(target).execTransactionFromModule(to, value, data, operation); +contract AgentTradingModule is Module { + using GPv2Order for bytes; + + event SetOrderTradeable( + bytes indexed orderUid, + address indexed sellToken, + address indexed buyToken, + uint256 sellAmount, + uint256 buyAmount + ); + + ITokenAllowlist internal allowlist; + bytes32 internal domainSeparator; + + constructor(address _owner, address _avatar, address _target, address _tokenAllowlist, bytes32 _domainSeparator) { + bytes memory initParams = abi.encode(_owner, _avatar, _target, _tokenAllowlist, _domainSeparator); + setUp(initParams); + } + + function setUp(bytes memory initParams) public override initializer { + (address _owner, address _avatar, address _target, address _tokenAllowlist, bytes32 _domainSeparator) = + abi.decode(initParams, (address, address, address, address, bytes32)); + + require(_avatar != address(0)); + require(_target != address(0)); + + __Ownable_init(msg.sender); + + setAvatar(_avatar); + setTarget(_target); + allowlist = ITokenAllowlist(_tokenAllowlist); + domainSeparator = _domainSeparator; + + transferOwnership(_owner); } - function execAndReturnData(address to, uint256 value, bytes memory data, Enum.Operation operation) - internal - virtual - override - returns (bool success, bytes memory returnData) - { - (success, returnData) = IAvatar(target).execTransactionFromModuleReturnData(to, value, data, operation); + function setOrderTradeable(bytes memory orderUid, GPv2Order.Data memory order) external { + bytes memory uid; + uid.packOrderUidParams(GPv2Order.hash(order, domainSeparator), owner(), order.validTo); + + // Order UID validation + require(keccak256(orderUid) == keccak256(uid)); + + // Order tokens validation + require(allowlist.isOrderAllowed(address(order.sellToken), address(order.buyToken))); + + // @todo validation: + // - balances + // - paused + // - trade frequency + + bytes memory data = abi.encodeWithSignature("setPreSignature(bytes,bool)", orderUid, true); + require(exec(address(allowlist), 0, data, Enum.Operation.Call)); + + emit SetOrderTradeable( + orderUid, address(order.sellToken), address(order.buyToken), order.sellAmount, order.buyAmount + ); } } From a16f80dfb32d401044e4bbcd93eb6847eec867bc Mon Sep 17 00:00:00 2001 From: lumoswiz Date: Thu, 6 Feb 2025 14:08:19 +0100 Subject: [PATCH 09/37] chore: install all contract dependencies --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 40b94e3..071131b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -94,7 +94,7 @@ jobs: ape-plugins-list: 'solidity==0.8.5 alchemy==0.8.7 etherscan==0.8.4 foundry==0.8.7' - name: 'Install contract dependencies' - run: 'ape pm install gh:Vectorized/solady --name solady --ref v0.1.3' + run: 'ape pm install' - name: 'Compile contracts' run: 'ape compile --force' From 7b46444d72d2a94555edf5f18780b009e105acb7 Mon Sep 17 00:00:00 2001 From: lumoswiz Date: Thu, 6 Feb 2025 14:10:54 +0100 Subject: [PATCH 10/37] chore: add cowprotocol dependency and update solidity version --- cow-trader/ape-config.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cow-trader/ape-config.yaml b/cow-trader/ape-config.yaml index e8a6a9d..f94660b 100644 --- a/cow-trader/ape-config.yaml +++ b/cow-trader/ape-config.yaml @@ -11,9 +11,12 @@ plugins: version: 0.8.5 solidity: - version: 0.8.25 + version: 0.8.27 dependencies: - name: Solady github: Vectorized/solady ref: v0.1.3 + - name: cowprotocol + github: cowprotocol/contracts + ref: main From 6895da1e791bcce60f1229fe8661e3e1771145a2 Mon Sep 17 00:00:00 2001 From: lumoswiz Date: Thu, 6 Feb 2025 23:20:36 +0100 Subject: [PATCH 11/37] refactor: remove dependencies from ape-config.yaml --- cow-trader/ape-config.yaml | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/cow-trader/ape-config.yaml b/cow-trader/ape-config.yaml index f94660b..c4ff42d 100644 --- a/cow-trader/ape-config.yaml +++ b/cow-trader/ape-config.yaml @@ -9,14 +9,3 @@ plugins: version: 0.8.7 - name: solidity version: 0.8.5 - -solidity: - version: 0.8.27 - -dependencies: - - name: Solady - github: Vectorized/solady - ref: v0.1.3 - - name: cowprotocol - github: cowprotocol/contracts - ref: main From 2d6f34d5cf79d31f4d63440885f9128ddd273d1d Mon Sep 17 00:00:00 2001 From: lumoswiz Date: Thu, 6 Feb 2025 23:24:20 +0100 Subject: [PATCH 12/37] chore: foundry init --- smart-contract-infra/README.md | 67 ++++++++++++++++++++--- smart-contract-infra/foundry.toml | 6 ++ smart-contract-infra/script/Counter.s.sol | 19 +++++++ smart-contract-infra/src/Counter.sol | 14 +++++ smart-contract-infra/test/Counter.t.sol | 24 ++++++++ 5 files changed, 122 insertions(+), 8 deletions(-) create mode 100644 smart-contract-infra/foundry.toml create mode 100644 smart-contract-infra/script/Counter.s.sol create mode 100644 smart-contract-infra/src/Counter.sol create mode 100644 smart-contract-infra/test/Counter.t.sol diff --git a/smart-contract-infra/README.md b/smart-contract-infra/README.md index e74684b..9265b45 100644 --- a/smart-contract-infra/README.md +++ b/smart-contract-infra/README.md @@ -1,15 +1,66 @@ -# smart-contract-infra +## Foundry -To install dependencies: +**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.** -```bash -bun install +Foundry consists of: + +- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools). +- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data. +- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network. +- **Chisel**: Fast, utilitarian, and verbose solidity REPL. + +## Documentation + +https://book.getfoundry.sh/ + +## Usage + +### Build + +```shell +$ forge build +``` + +### Test + +```shell +$ forge test +``` + +### Format + +```shell +$ forge fmt ``` -To run: +### Gas Snapshots -```bash -bun run index.ts +```shell +$ forge snapshot ``` -This project was created using `bun init` in bun v1.1.31. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime. +### Anvil + +```shell +$ anvil +``` + +### Deploy + +```shell +$ forge script script/Counter.s.sol:CounterScript --rpc-url --private-key +``` + +### Cast + +```shell +$ cast +``` + +### Help + +```shell +$ forge --help +$ anvil --help +$ cast --help +``` diff --git a/smart-contract-infra/foundry.toml b/smart-contract-infra/foundry.toml new file mode 100644 index 0000000..25b918f --- /dev/null +++ b/smart-contract-infra/foundry.toml @@ -0,0 +1,6 @@ +[profile.default] +src = "src" +out = "out" +libs = ["lib"] + +# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options diff --git a/smart-contract-infra/script/Counter.s.sol b/smart-contract-infra/script/Counter.s.sol new file mode 100644 index 0000000..cdc1fe9 --- /dev/null +++ b/smart-contract-infra/script/Counter.s.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {Script, console} from "forge-std/Script.sol"; +import {Counter} from "../src/Counter.sol"; + +contract CounterScript is Script { + Counter public counter; + + function setUp() public {} + + function run() public { + vm.startBroadcast(); + + counter = new Counter(); + + vm.stopBroadcast(); + } +} diff --git a/smart-contract-infra/src/Counter.sol b/smart-contract-infra/src/Counter.sol new file mode 100644 index 0000000..aded799 --- /dev/null +++ b/smart-contract-infra/src/Counter.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +contract Counter { + uint256 public number; + + function setNumber(uint256 newNumber) public { + number = newNumber; + } + + function increment() public { + number++; + } +} diff --git a/smart-contract-infra/test/Counter.t.sol b/smart-contract-infra/test/Counter.t.sol new file mode 100644 index 0000000..54b724f --- /dev/null +++ b/smart-contract-infra/test/Counter.t.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {Test, console} from "forge-std/Test.sol"; +import {Counter} from "../src/Counter.sol"; + +contract CounterTest is Test { + Counter public counter; + + function setUp() public { + counter = new Counter(); + counter.setNumber(0); + } + + function test_Increment() public { + counter.increment(); + assertEq(counter.number(), 1); + } + + function testFuzz_SetNumber(uint256 x) public { + counter.setNumber(x); + assertEq(counter.number(), x); + } +} From 516f40111fb2c2c15e3435e465541183abded109 Mon Sep 17 00:00:00 2001 From: lumoswiz Date: Thu, 6 Feb 2025 23:33:14 +0100 Subject: [PATCH 13/37] chore: add solhint config --- smart-contract-infra/.solhint.json | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 smart-contract-infra/.solhint.json diff --git a/smart-contract-infra/.solhint.json b/smart-contract-infra/.solhint.json new file mode 100644 index 0000000..02948bf --- /dev/null +++ b/smart-contract-infra/.solhint.json @@ -0,0 +1,28 @@ +{ + "extends": "solhint:recommended", + "rules": { + "code-complexity": [ + "error", + 8 + ], + "compiler-version": [ + "error", + ">=0.8.28" + ], + "func-name-mixedcase": "off", + "func-visibility": [ + "error", + { + "ignoreConstructors": true + } + ], + "max-line-length": [ + "error", + 120 + ], + "named-parameters-mapping": "warn", + "no-console": "off", + "not-rely-on-time": "off", + "one-contract-per-file": "off" + } +} \ No newline at end of file From 37649e204e54ab26361e815b58828e99d55cef5b Mon Sep 17 00:00:00 2001 From: lumoswiz Date: Thu, 6 Feb 2025 23:33:44 +0100 Subject: [PATCH 14/37] chore: add foundry.toml template setup --- smart-contract-infra/foundry.toml | 39 ++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/smart-contract-infra/foundry.toml b/smart-contract-infra/foundry.toml index 25b918f..89e0827 100644 --- a/smart-contract-infra/foundry.toml +++ b/smart-contract-infra/foundry.toml @@ -1,6 +1,39 @@ [profile.default] -src = "src" +auto_detect_solc = false +block_timestamp = 1_738_368_000 # Feb 1, 2025 at 00:00 GMT +bytecode_hash = "none" +evm_version = "shanghai" +fuzz = { runs = 1_000 } +gas_reports = ["*"] +optimizer = true +optimizer_runs = 10_000 out = "out" -libs = ["lib"] +script = "script" +solc = "0.8.28" +src = "src" +test = "tests" + +[profile.ci] +fuzz = { runs = 10_000 } +verbosity = 4 + +[etherscan] +mainnet = { key = "${API_KEY_ETHERSCAN}" } + +[fmt] +bracket_spacing = true +int_types = "long" +line_length = 120 +multiline_func_header = "all" +number_underscore = "thousands" +quote_style = "double" +tab_width = 4 +wrap_comments = true -# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options +[rpc_endpoints] +arbitrum = "https://arbitrum-one-rpc.publicnode.com" +base = "https://mainnet.base.org" +gnosis_chain = "https://rpc.gnosischain.com" +localhost = "http://localhost:8545" +mainnet = "https://eth-mainnet.g.alchemy.com/v2/${API_KEY_ALCHEMY}" +sepolia = "https://ethereum-sepolia-rpc.publicnode.com" From 81c8bc1ab5643db5394e0cd72a03a92bef0ea81e Mon Sep 17 00:00:00 2001 From: lumoswiz Date: Thu, 6 Feb 2025 23:34:19 +0100 Subject: [PATCH 15/37] chore: add depdendencies and helper bun scripts --- smart-contract-infra/bun.lockb | Bin 3068 -> 30319 bytes smart-contract-infra/package.json | 22 +++++++++++++++++----- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/smart-contract-infra/bun.lockb b/smart-contract-infra/bun.lockb index bcb25bbbb042d23baa25dfa01c9e51d2fa2d461c..acfc41184f9155c385533d8be7ae1e6023c5148a 100755 GIT binary patch literal 30319 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 e9u2$Q4OjRptUT?tw literal 3068 zcmd5;eN0i=+r;T!vDjUPPExc-vMkXc*BKOPA z50$opLs9E?8fzv}_8Zp@ZU~kfLd^Epk) zjp8wTU+E*G;pOU#vdGq_HU~2aT_dKW9ko4`g~uGitXmG9PHd?&>`<;RQSj773FvNx=vzIhWiUM!v`ymo3qrBeJ=-WFB#pkucB$Dc4yC<20vb$Ux-`c9vTQ~J!N_< zvG?57=FlsjZ`$D;e zYZrPWaRS!7Ob&!cJctANfZi|SLAq4nFv0?kM3#`LGh}(<;8z*uebV0fRk-fsIxZEv zEIi5a3oigydZ{Qo8fbuqAaIX>Yra^d1v;REC*m#vSADT)ZybmLS`C5w3EUruMH_uS z5(MrraK`{K2B1R+EsiM_z7Xp>uaqO*dq@@}74RfWTU_ZpOVM_tLc0)zxcMM_E->W? z9TdDn7%%N&;UYM

RNhUQs|l~+-;jkRpy3-d@$4f{9L&f$L!V0Cc0JfFwKtJN%N zr#PN11azL;!2yP~&zq@L^HWtc(8ECT_M#=!5m z@_bI1&`b`w1e>1~rrJ^`9>Jn?V-;rD`q_mKTwH4x;lpl`rA+ L2qPreeW(5b(d0Y; diff --git a/smart-contract-infra/package.json b/smart-contract-infra/package.json index 1890b5a..81091ff 100644 --- a/smart-contract-infra/package.json +++ b/smart-contract-infra/package.json @@ -2,13 +2,25 @@ "name": "smart-contract-infra", "module": "index.ts", "type": "module", + "dependencies": { + "@gnosis-guild/zodiac": "4.0.3", + "@gnosis.pm/safe-contracts": "1.3.0", + "@openzeppelin/contracts-upgradeable": "5.2.0" + }, "devDependencies": { - "@types/bun": "latest", - "prettier": "^3.0.0" + "forge-std": "github:foundry-rs/forge-std#v1.8.1", + "prettier": "^3.0.0", + "solhint": "^3.6.2" }, "scripts": { - "lint": "bun run prettier:check", + "clean": "rm -rf cache out", + "build": "forge build", + "lint": "bun run lint:sol && bun run prettier:check", + "lint:sol": "forge fmt --check && bun solhint \"{script,src,tests}/**/*.sol\"", "prettier:check": "prettier --check \"**/*.{json,md,yml}\" --ignore-path \".prettierignore\"", - "prettier:write": "prettier --write \"**/*.{json,md,yml}\" --ignore-path \".prettierignore\"" + "prettier:write": "prettier --write \"**/*.{json,md,yml}\" --ignore-path \".prettierignore\"", + "test": "forge test", + "test:coverage": "forge coverage", + "test:coverage:report": "forge coverage --report lcov && genhtml lcov.info --branch-coverage --output-dir coverage" } -} +} \ No newline at end of file From 84d07427fc73a6d31d9e1145f79cab34ed171655 Mon Sep 17 00:00:00 2001 From: lumoswiz Date: Thu, 6 Feb 2025 23:35:48 +0100 Subject: [PATCH 16/37] chore: add remappings --- smart-contract-infra/remappings.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 smart-contract-infra/remappings.txt diff --git a/smart-contract-infra/remappings.txt b/smart-contract-infra/remappings.txt new file mode 100644 index 0000000..ea88015 --- /dev/null +++ b/smart-contract-infra/remappings.txt @@ -0,0 +1,7 @@ +forge-std/=lib/forge-std/src/ +zodiac/=lib/zodiac/contracts/ +@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/ +@openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/ +openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/ +openzeppelin-contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/ +@gnosis.pm/safe-contracts=lib/safe-smart-account/ From 7ff0c7c3dc39d2cbd7f2a9bd86dfe53252e2d0e5 Mon Sep 17 00:00:00 2001 From: lumoswiz Date: Thu, 6 Feb 2025 23:36:56 +0100 Subject: [PATCH 17/37] chore: remove forge init template files --- smart-contract-infra/script/Counter.s.sol | 19 ------------------ smart-contract-infra/src/Counter.sol | 14 ------------- smart-contract-infra/test/Counter.t.sol | 24 ----------------------- 3 files changed, 57 deletions(-) delete mode 100644 smart-contract-infra/script/Counter.s.sol delete mode 100644 smart-contract-infra/src/Counter.sol delete mode 100644 smart-contract-infra/test/Counter.t.sol diff --git a/smart-contract-infra/script/Counter.s.sol b/smart-contract-infra/script/Counter.s.sol deleted file mode 100644 index cdc1fe9..0000000 --- a/smart-contract-infra/script/Counter.s.sol +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import {Script, console} from "forge-std/Script.sol"; -import {Counter} from "../src/Counter.sol"; - -contract CounterScript is Script { - Counter public counter; - - function setUp() public {} - - function run() public { - vm.startBroadcast(); - - counter = new Counter(); - - vm.stopBroadcast(); - } -} diff --git a/smart-contract-infra/src/Counter.sol b/smart-contract-infra/src/Counter.sol deleted file mode 100644 index aded799..0000000 --- a/smart-contract-infra/src/Counter.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -contract Counter { - uint256 public number; - - function setNumber(uint256 newNumber) public { - number = newNumber; - } - - function increment() public { - number++; - } -} diff --git a/smart-contract-infra/test/Counter.t.sol b/smart-contract-infra/test/Counter.t.sol deleted file mode 100644 index 54b724f..0000000 --- a/smart-contract-infra/test/Counter.t.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import {Test, console} from "forge-std/Test.sol"; -import {Counter} from "../src/Counter.sol"; - -contract CounterTest is Test { - Counter public counter; - - function setUp() public { - counter = new Counter(); - counter.setNumber(0); - } - - function test_Increment() public { - counter.increment(); - assertEq(counter.number(), 1); - } - - function testFuzz_SetNumber(uint256 x) public { - counter.setNumber(x); - assertEq(counter.number(), x); - } -} From 36af254865415739d1c7bb8316ed1dce8985a4d2 Mon Sep 17 00:00:00 2001 From: lumoswiz Date: Thu, 6 Feb 2025 23:39:16 +0100 Subject: [PATCH 18/37] refactor: remove smart contracts + tests from cow-trade/ --- cow-trader/contracts/AgentTradingModule.sol | 68 ------ cow-trader/contracts/TokenAllowlist.sol | 47 ---- .../zodiac/contracts/core/Module.sol | 64 ----- .../contracts/factory/FactoryFriendly.sol | 10 - .../zodiac/contracts/guard/BaseGuard.sol | 33 --- .../zodiac/contracts/guard/Guardable.sol | 33 --- .../zodiac/contracts/interfaces/IAvatar.sol | 65 ----- .../zodiac/contracts/interfaces/IGuard.sol | 22 -- .../safe-contracts/contracts/common/Enum.sol | 11 - .../access/OwnableUpgradeable.sol | 119 --------- .../proxy/utils/Initializable.sol | 228 ------------------ .../utils/ContextUpgradeable.sol | 34 --- .../contracts/utils/introspection/IERC165.sol | 25 -- .../src/contracts/interfaces/IERC20.sol | 98 -------- .../src/contracts/libraries/GPv2Order.sol | 227 ----------------- .../contracts/interfaces/ITokenAllowlist.sol | 6 - cow-trader/tests/test_allowlist.py | 105 -------- 17 files changed, 1195 deletions(-) delete mode 100644 cow-trader/contracts/AgentTradingModule.sol delete mode 100644 cow-trader/contracts/TokenAllowlist.sol delete mode 100644 cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/core/Module.sol delete mode 100644 cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/factory/FactoryFriendly.sol delete mode 100644 cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/guard/BaseGuard.sol delete mode 100644 cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/guard/Guardable.sol delete mode 100644 cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/interfaces/IAvatar.sol delete mode 100644 cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/interfaces/IGuard.sol delete mode 100644 cow-trader/contracts/dependencies/@gnosis.pm/safe-contracts/contracts/common/Enum.sol delete mode 100644 cow-trader/contracts/dependencies/@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol delete mode 100644 cow-trader/contracts/dependencies/@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol delete mode 100644 cow-trader/contracts/dependencies/@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol delete mode 100644 cow-trader/contracts/dependencies/@openzeppelin/contracts/utils/introspection/IERC165.sol delete mode 100644 cow-trader/contracts/dependencies/cowprotocol/contracts/src/contracts/interfaces/IERC20.sol delete mode 100644 cow-trader/contracts/dependencies/cowprotocol/contracts/src/contracts/libraries/GPv2Order.sol delete mode 100644 cow-trader/contracts/interfaces/ITokenAllowlist.sol delete mode 100644 cow-trader/tests/test_allowlist.py diff --git a/cow-trader/contracts/AgentTradingModule.sol b/cow-trader/contracts/AgentTradingModule.sol deleted file mode 100644 index 672d0db..0000000 --- a/cow-trader/contracts/AgentTradingModule.sol +++ /dev/null @@ -1,68 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.25; - -import {Enum} from "./dependencies/@gnosis.pm/safe-contracts/contracts/common/Enum.sol"; -import {Module} from "./dependencies/@gnosis-guild/zodiac/contracts/core/Module.sol"; -import {IAvatar} from "./dependencies/@gnosis-guild/zodiac/contracts/interfaces/IAvatar.sol"; -import {GPv2Order} from "./dependencies/cowprotocol/contracts/src/contracts/libraries/GPv2Order.sol"; -import {ITokenAllowlist} from "./interfaces/ITokenAllowlist.sol"; - -contract AgentTradingModule is Module { - using GPv2Order for bytes; - - event SetOrderTradeable( - bytes indexed orderUid, - address indexed sellToken, - address indexed buyToken, - uint256 sellAmount, - uint256 buyAmount - ); - - ITokenAllowlist internal allowlist; - bytes32 internal domainSeparator; - - constructor(address _owner, address _avatar, address _target, address _tokenAllowlist, bytes32 _domainSeparator) { - bytes memory initParams = abi.encode(_owner, _avatar, _target, _tokenAllowlist, _domainSeparator); - setUp(initParams); - } - - function setUp(bytes memory initParams) public override initializer { - (address _owner, address _avatar, address _target, address _tokenAllowlist, bytes32 _domainSeparator) = - abi.decode(initParams, (address, address, address, address, bytes32)); - - require(_avatar != address(0)); - require(_target != address(0)); - - __Ownable_init(msg.sender); - - setAvatar(_avatar); - setTarget(_target); - allowlist = ITokenAllowlist(_tokenAllowlist); - domainSeparator = _domainSeparator; - - transferOwnership(_owner); - } - - function setOrderTradeable(bytes memory orderUid, GPv2Order.Data memory order) external { - bytes memory uid; - uid.packOrderUidParams(GPv2Order.hash(order, domainSeparator), owner(), order.validTo); - - // Order UID validation - require(keccak256(orderUid) == keccak256(uid)); - - // Order tokens validation - require(allowlist.isOrderAllowed(address(order.sellToken), address(order.buyToken))); - - // @todo validation: - // - balances - // - paused - // - trade frequency - - bytes memory data = abi.encodeWithSignature("setPreSignature(bytes,bool)", orderUid, true); - require(exec(address(allowlist), 0, data, Enum.Operation.Call)); - - emit SetOrderTradeable( - orderUid, address(order.sellToken), address(order.buyToken), order.sellAmount, order.buyAmount - ); - } -} diff --git a/cow-trader/contracts/TokenAllowlist.sol b/cow-trader/contracts/TokenAllowlist.sol deleted file mode 100644 index 18ade93..0000000 --- a/cow-trader/contracts/TokenAllowlist.sol +++ /dev/null @@ -1,47 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.25; - -import {Ownable} from "solady/src/auth/Ownable.sol"; -import {EnumerableSetLib} from "solady/src/utils/EnumerableSetLib.sol"; - -contract TokenAllowlist is Ownable { - using EnumerableSetLib for EnumerableSetLib.AddressSet; - - EnumerableSetLib.AddressSet internal allowlist; - - constructor() { - _initializeOwner(msg.sender); - } - - function addToken(address token) external onlyOwner { - allowlist.add(token); - } - - function addTokensBatch(address[] memory tokens) external onlyOwner { - for (uint256 i; i < tokens.length; ++i) { - allowlist.add(tokens[i]); - } - } - - function removeToken(address token) external onlyOwner { - allowlist.remove(token); - } - - function removeTokensBatch(address[] memory tokens) external onlyOwner { - for (uint256 i; i < tokens.length; ++i) { - allowlist.remove(tokens[i]); - } - } - - function allowedTokens() external view returns (address[] memory) { - return allowlist.values(); - } - - function isAllowed(address token) public view returns (bool) { - return allowlist.contains(token); - } - - function isOrderAllowed(address sellToken, address buyToken) external view returns (bool) { - return isAllowed(sellToken) && isAllowed(buyToken); - } -} diff --git a/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/core/Module.sol b/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/core/Module.sol deleted file mode 100644 index 509549f..0000000 --- a/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/core/Module.sol +++ /dev/null @@ -1,64 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity >=0.7.0 <0.9.0; - -import {Enum} from "../../../../@gnosis.pm/safe-contracts/contracts/common/Enum.sol"; - -import {FactoryFriendly} from "../factory/FactoryFriendly.sol"; -import {IAvatar} from "../interfaces/IAvatar.sol"; - -/// @title Module Interface - A contract that can pass messages to a Module Manager contract if enabled by that contract. -abstract contract Module is FactoryFriendly { - /// @dev Address that will ultimately execute function calls. - address public avatar; - /// @dev Address that this module will pass transactions to. - address public target; - - /// @dev Emitted each time the avatar is set. - event AvatarSet(address indexed previousAvatar, address indexed newAvatar); - /// @dev Emitted each time the Target is set. - event TargetSet(address indexed previousTarget, address indexed newTarget); - - /// @dev Sets the avatar to a new avatar (`newAvatar`). - /// @notice Can only be called by the current owner. - function setAvatar(address _avatar) public onlyOwner { - address previousAvatar = avatar; - avatar = _avatar; - emit AvatarSet(previousAvatar, _avatar); - } - - /// @dev Sets the target to a new target (`newTarget`). - /// @notice Can only be called by the current owner. - function setTarget(address _target) public onlyOwner { - address previousTarget = target; - target = _target; - emit TargetSet(previousTarget, _target); - } - - /// @dev Passes a transaction to be executed by the avatar. - /// @notice Can only be called by this contract. - /// @param to Destination address of module transaction. - /// @param value Ether value of module transaction. - /// @param data Data payload of module transaction. - /// @param operation Operation type of module transaction: 0 == call, 1 == delegate call. - function exec(address to, uint256 value, bytes memory data, Enum.Operation operation) - internal - virtual - returns (bool success) - { - return IAvatar(target).execTransactionFromModule(to, value, data, operation); - } - - /// @dev Passes a transaction to be executed by the target and returns data. - /// @notice Can only be called by this contract. - /// @param to Destination address of module transaction. - /// @param value Ether value of module transaction. - /// @param data Data payload of module transaction. - /// @param operation Operation type of module transaction: 0 == call, 1 == delegate call. - function execAndReturnData(address to, uint256 value, bytes memory data, Enum.Operation operation) - internal - virtual - returns (bool success, bytes memory returnData) - { - return IAvatar(target).execTransactionFromModuleReturnData(to, value, data, operation); - } -} diff --git a/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/factory/FactoryFriendly.sol b/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/factory/FactoryFriendly.sol deleted file mode 100644 index 491509b..0000000 --- a/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/factory/FactoryFriendly.sol +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only - -/// @title Zodiac FactoryFriendly - A contract that allows other contracts to be initializable and pass bytes as arguments to define contract state -pragma solidity >=0.7.0 <0.9.0; - -import {OwnableUpgradeable} from "../../../../@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; - -abstract contract FactoryFriendly is OwnableUpgradeable { - function setUp(bytes memory initializeParams) public virtual; -} diff --git a/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/guard/BaseGuard.sol b/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/guard/BaseGuard.sol deleted file mode 100644 index 11dbc97..0000000 --- a/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/guard/BaseGuard.sol +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity >=0.7.0 <0.9.0; - -import {Enum} from "../../../../@gnosis.pm/safe-contracts/contracts/common/Enum.sol"; -import {IERC165} from "../../../../@openzeppelin/contracts/utils/introspection/IERC165.sol"; - -import {IGuard} from "../interfaces/IGuard.sol"; - -abstract contract BaseGuard is IERC165 { - function supportsInterface(bytes4 interfaceId) external pure override returns (bool) { - return interfaceId == type(IGuard).interfaceId // 0xe6d7a83a - || interfaceId == type(IERC165).interfaceId; // 0x01ffc9a7 - } - - /// @dev Module transactions only use the first four parameters: to, value, data, and operation. - /// Module.sol hardcodes the remaining parameters as 0 since they are not used for module transactions. - /// @notice This interface is used to maintain compatibilty with Gnosis Safe transaction guards. - function checkTransaction( - address to, - uint256 value, - bytes memory data, - Enum.Operation operation, - uint256 safeTxGas, - uint256 baseGas, - uint256 gasPrice, - address gasToken, - address payable refundReceiver, - bytes memory signatures, - address msgSender - ) external virtual; - - function checkAfterExecution(bytes32 txHash, bool success) external virtual; -} diff --git a/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/guard/Guardable.sol b/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/guard/Guardable.sol deleted file mode 100644 index fc1f18e..0000000 --- a/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/guard/Guardable.sol +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity >=0.7.0 <0.9.0; - -import {OwnableUpgradeable} from "../../../../@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; - -import {BaseGuard} from "../guard/BaseGuard.sol"; -import {IGuard} from "../interfaces/IGuard.sol"; - -/// @title Guardable - A contract that manages fallback calls made to this contract -contract Guardable is OwnableUpgradeable { - address public guard; - - event ChangedGuard(address guard); - - /// `guard_` does not implement IERC165. - error NotIERC165Compliant(address guard_); - - /// @dev Set a guard that checks transactions before execution. - /// @param _guard The address of the guard to be used or the 0 address to disable the guard. - function setGuard(address _guard) external onlyOwner { - if (_guard != address(0)) { - if (!BaseGuard(_guard).supportsInterface(type(IGuard).interfaceId)) { - revert NotIERC165Compliant(_guard); - } - } - guard = _guard; - emit ChangedGuard(guard); - } - - function getGuard() external view returns (address _guard) { - return guard; - } -} diff --git a/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/interfaces/IAvatar.sol b/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/interfaces/IAvatar.sol deleted file mode 100644 index bcfe7bd..0000000 --- a/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/interfaces/IAvatar.sol +++ /dev/null @@ -1,65 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only - -/// @title Zodiac Avatar - A contract that manages modules that can execute transactions via this contract. -pragma solidity >=0.7.0 <0.9.0; - -import {Enum} from "../../../../@gnosis.pm/safe-contracts/contracts/common/Enum.sol"; - -interface IAvatar { - event EnabledModule(address module); - event DisabledModule(address module); - event ExecutionFromModuleSuccess(address indexed module); - event ExecutionFromModuleFailure(address indexed module); - - /// @dev Enables a module on the avatar. - /// @notice Can only be called by the avatar. - /// @notice Modules should be stored as a linked list. - /// @notice Must emit EnabledModule(address module) if successful. - /// @param module Module to be enabled. - function enableModule(address module) external; - - /// @dev Disables a module on the avatar. - /// @notice Can only be called by the avatar. - /// @notice Must emit DisabledModule(address module) if successful. - /// @param prevModule Address that pointed to the module to be removed in the linked list - /// @param module Module to be removed. - function disableModule(address prevModule, address module) external; - - /// @dev Allows a Module to execute a transaction. - /// @notice Can only be called by an enabled module. - /// @notice Must emit ExecutionFromModuleSuccess(address module) if successful. - /// @notice Must emit ExecutionFromModuleFailure(address module) if unsuccessful. - /// @param to Destination address of module transaction. - /// @param value Ether value of module transaction. - /// @param data Data payload of module transaction. - /// @param operation Operation type of module transaction: 0 == call, 1 == delegate call. - function execTransactionFromModule(address to, uint256 value, bytes memory data, Enum.Operation operation) - external - returns (bool success); - - /// @dev Allows a Module to execute a transaction and return data - /// @notice Can only be called by an enabled module. - /// @notice Must emit ExecutionFromModuleSuccess(address module) if successful. - /// @notice Must emit ExecutionFromModuleFailure(address module) if unsuccessful. - /// @param to Destination address of module transaction. - /// @param value Ether value of module transaction. - /// @param data Data payload of module transaction. - /// @param operation Operation type of module transaction: 0 == call, 1 == delegate call. - function execTransactionFromModuleReturnData(address to, uint256 value, bytes memory data, Enum.Operation operation) - external - returns (bool success, bytes memory returnData); - - /// @dev Returns if an module is enabled - /// @return True if the module is enabled - function isModuleEnabled(address module) external view returns (bool); - - /// @dev Returns array of modules. - /// @param start Start of the page. - /// @param pageSize Maximum number of modules that should be returned. - /// @return array Array of modules. - /// @return next Start of the next page. - function getModulesPaginated(address start, uint256 pageSize) - external - view - returns (address[] memory array, address next); -} diff --git a/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/interfaces/IGuard.sol b/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/interfaces/IGuard.sol deleted file mode 100644 index 2b96428..0000000 --- a/cow-trader/contracts/dependencies/@gnosis-guild/zodiac/contracts/interfaces/IGuard.sol +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity >=0.7.0 <0.9.0; - -import {Enum} from "../../../../@gnosis.pm/safe-contracts/contracts/common/Enum.sol"; - -interface IGuard { - function checkTransaction( - address to, - uint256 value, - bytes memory data, - Enum.Operation operation, - uint256 safeTxGas, - uint256 baseGas, - uint256 gasPrice, - address gasToken, - address payable refundReceiver, - bytes memory signatures, - address msgSender - ) external; - - function checkAfterExecution(bytes32 txHash, bool success) external; -} diff --git a/cow-trader/contracts/dependencies/@gnosis.pm/safe-contracts/contracts/common/Enum.sol b/cow-trader/contracts/dependencies/@gnosis.pm/safe-contracts/contracts/common/Enum.sol deleted file mode 100644 index 4b7c8ca..0000000 --- a/cow-trader/contracts/dependencies/@gnosis.pm/safe-contracts/contracts/common/Enum.sol +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity >=0.7.0 <0.9.0; - -/// @title Enum - Collection of enums -/// @author Richard Meissner - -contract Enum { - enum Operation { - Call, - DelegateCall - } -} diff --git a/cow-trader/contracts/dependencies/@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol b/cow-trader/contracts/dependencies/@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol deleted file mode 100644 index fae3fef..0000000 --- a/cow-trader/contracts/dependencies/@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol +++ /dev/null @@ -1,119 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol) - -pragma solidity ^0.8.20; - -import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol"; -import {Initializable} from "../proxy/utils/Initializable.sol"; - -/** - * @dev Contract module which provides a basic access control mechanism, where - * there is an account (an owner) that can be granted exclusive access to - * specific functions. - * - * The initial owner is set to the address provided by the deployer. This can - * later be changed with {transferOwnership}. - * - * This module is used through inheritance. It will make available the modifier - * `onlyOwner`, which can be applied to your functions to restrict their use to - * the owner. - */ -abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable { - /// @custom:storage-location erc7201:openzeppelin.storage.Ownable - struct OwnableStorage { - address _owner; - } - - // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable")) - 1)) & ~bytes32(uint256(0xff)) - bytes32 private constant OwnableStorageLocation = 0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300; - - function _getOwnableStorage() private pure returns (OwnableStorage storage $) { - assembly { - $.slot := OwnableStorageLocation - } - } - - /** - * @dev The caller account is not authorized to perform an operation. - */ - error OwnableUnauthorizedAccount(address account); - - /** - * @dev The owner is not a valid owner account. (eg. `address(0)`) - */ - error OwnableInvalidOwner(address owner); - - event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); - - /** - * @dev Initializes the contract setting the address provided by the deployer as the initial owner. - */ - function __Ownable_init(address initialOwner) internal onlyInitializing { - __Ownable_init_unchained(initialOwner); - } - - function __Ownable_init_unchained(address initialOwner) internal onlyInitializing { - if (initialOwner == address(0)) { - revert OwnableInvalidOwner(address(0)); - } - _transferOwnership(initialOwner); - } - - /** - * @dev Throws if called by any account other than the owner. - */ - modifier onlyOwner() { - _checkOwner(); - _; - } - - /** - * @dev Returns the address of the current owner. - */ - function owner() public view virtual returns (address) { - OwnableStorage storage $ = _getOwnableStorage(); - return $._owner; - } - - /** - * @dev Throws if the sender is not the owner. - */ - function _checkOwner() internal view virtual { - if (owner() != _msgSender()) { - revert OwnableUnauthorizedAccount(_msgSender()); - } - } - - /** - * @dev Leaves the contract without owner. It will not be possible to call - * `onlyOwner` functions. Can only be called by the current owner. - * - * NOTE: Renouncing ownership will leave the contract without an owner, - * thereby disabling any functionality that is only available to the owner. - */ - function renounceOwnership() public virtual onlyOwner { - _transferOwnership(address(0)); - } - - /** - * @dev Transfers ownership of the contract to a new account (`newOwner`). - * Can only be called by the current owner. - */ - function transferOwnership(address newOwner) public virtual onlyOwner { - if (newOwner == address(0)) { - revert OwnableInvalidOwner(address(0)); - } - _transferOwnership(newOwner); - } - - /** - * @dev Transfers ownership of the contract to a new account (`newOwner`). - * Internal function without access restriction. - */ - function _transferOwnership(address newOwner) internal virtual { - OwnableStorage storage $ = _getOwnableStorage(); - address oldOwner = $._owner; - $._owner = newOwner; - emit OwnershipTransferred(oldOwner, newOwner); - } -} diff --git a/cow-trader/contracts/dependencies/@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol b/cow-trader/contracts/dependencies/@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol deleted file mode 100644 index b3d82b5..0000000 --- a/cow-trader/contracts/dependencies/@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol +++ /dev/null @@ -1,228 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol) - -pragma solidity ^0.8.20; - -/** - * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed - * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an - * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer - * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. - * - * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be - * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in - * case an upgrade adds a module that needs to be initialized. - * - * For example: - * - * [.hljs-theme-light.nopadding] - * ```solidity - * contract MyToken is ERC20Upgradeable { - * function initialize() initializer public { - * __ERC20_init("MyToken", "MTK"); - * } - * } - * - * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { - * function initializeV2() reinitializer(2) public { - * __ERC20Permit_init("MyToken"); - * } - * } - * ``` - * - * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as - * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. - * - * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure - * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. - * - * [CAUTION] - * ==== - * Avoid leaving a contract uninitialized. - * - * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation - * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke - * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: - * - * [.hljs-theme-light.nopadding] - * ``` - * /// @custom:oz-upgrades-unsafe-allow constructor - * constructor() { - * _disableInitializers(); - * } - * ``` - * ==== - */ -abstract contract Initializable { - /** - * @dev Storage of the initializable contract. - * - * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions - * when using with upgradeable contracts. - * - * @custom:storage-location erc7201:openzeppelin.storage.Initializable - */ - struct InitializableStorage { - /** - * @dev Indicates that the contract has been initialized. - */ - uint64 _initialized; - /** - * @dev Indicates that the contract is in the process of being initialized. - */ - bool _initializing; - } - - // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff)) - bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00; - - /** - * @dev The contract is already initialized. - */ - error InvalidInitialization(); - - /** - * @dev The contract is not initializing. - */ - error NotInitializing(); - - /** - * @dev Triggered when the contract has been initialized or reinitialized. - */ - event Initialized(uint64 version); - - /** - * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, - * `onlyInitializing` functions can be used to initialize parent contracts. - * - * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any - * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in - * production. - * - * Emits an {Initialized} event. - */ - modifier initializer() { - // solhint-disable-next-line var-name-mixedcase - InitializableStorage storage $ = _getInitializableStorage(); - - // Cache values to avoid duplicated sloads - bool isTopLevelCall = !$._initializing; - uint64 initialized = $._initialized; - - // Allowed calls: - // - initialSetup: the contract is not in the initializing state and no previous version was - // initialized - // - construction: the contract is initialized at version 1 (no reininitialization) and the - // current contract is just being deployed - bool initialSetup = initialized == 0 && isTopLevelCall; - bool construction = initialized == 1 && address(this).code.length == 0; - - if (!initialSetup && !construction) { - revert InvalidInitialization(); - } - $._initialized = 1; - if (isTopLevelCall) { - $._initializing = true; - } - _; - if (isTopLevelCall) { - $._initializing = false; - emit Initialized(1); - } - } - - /** - * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the - * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be - * used to initialize parent contracts. - * - * A reinitializer may be used after the original initialization step. This is essential to configure modules that - * are added through upgrades and that require initialization. - * - * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer` - * cannot be nested. If one is invoked in the context of another, execution will revert. - * - * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in - * a contract, executing them in the right order is up to the developer or operator. - * - * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization. - * - * Emits an {Initialized} event. - */ - modifier reinitializer(uint64 version) { - // solhint-disable-next-line var-name-mixedcase - InitializableStorage storage $ = _getInitializableStorage(); - - if ($._initializing || $._initialized >= version) { - revert InvalidInitialization(); - } - $._initialized = version; - $._initializing = true; - _; - $._initializing = false; - emit Initialized(version); - } - - /** - * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the - * {initializer} and {reinitializer} modifiers, directly or indirectly. - */ - modifier onlyInitializing() { - _checkInitializing(); - _; - } - - /** - * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}. - */ - function _checkInitializing() internal view virtual { - if (!_isInitializing()) { - revert NotInitializing(); - } - } - - /** - * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. - * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized - * to any version. It is recommended to use this to lock implementation contracts that are designed to be called - * through proxies. - * - * Emits an {Initialized} event the first time it is successfully executed. - */ - function _disableInitializers() internal virtual { - // solhint-disable-next-line var-name-mixedcase - InitializableStorage storage $ = _getInitializableStorage(); - - if ($._initializing) { - revert InvalidInitialization(); - } - if ($._initialized != type(uint64).max) { - $._initialized = type(uint64).max; - emit Initialized(type(uint64).max); - } - } - - /** - * @dev Returns the highest version that has been initialized. See {reinitializer}. - */ - function _getInitializedVersion() internal view returns (uint64) { - return _getInitializableStorage()._initialized; - } - - /** - * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}. - */ - function _isInitializing() internal view returns (bool) { - return _getInitializableStorage()._initializing; - } - - /** - * @dev Returns a pointer to the storage namespace. - */ - // solhint-disable-next-line var-name-mixedcase - function _getInitializableStorage() private pure returns (InitializableStorage storage $) { - assembly { - $.slot := INITIALIZABLE_STORAGE - } - } -} diff --git a/cow-trader/contracts/dependencies/@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol b/cow-trader/contracts/dependencies/@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol deleted file mode 100644 index 0d1d4f5..0000000 --- a/cow-trader/contracts/dependencies/@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol) - -pragma solidity ^0.8.20; - -import {Initializable} from "../proxy/utils/Initializable.sol"; - -/** - * @dev Provides information about the current execution context, including the - * sender of the transaction and its data. While these are generally available - * via msg.sender and msg.data, they should not be accessed in such a direct - * manner, since when dealing with meta-transactions the account sending and - * paying for execution may not be the actual sender (as far as an application - * is concerned). - * - * This contract is only required for intermediate, library-like contracts. - */ -abstract contract ContextUpgradeable is Initializable { - function __Context_init() internal onlyInitializing {} - - function __Context_init_unchained() internal onlyInitializing {} - - function _msgSender() internal view virtual returns (address) { - return msg.sender; - } - - function _msgData() internal view virtual returns (bytes calldata) { - return msg.data; - } - - function _contextSuffixLength() internal view virtual returns (uint256) { - return 0; - } -} diff --git a/cow-trader/contracts/dependencies/@openzeppelin/contracts/utils/introspection/IERC165.sol b/cow-trader/contracts/dependencies/@openzeppelin/contracts/utils/introspection/IERC165.sol deleted file mode 100644 index 719ec35..0000000 --- a/cow-trader/contracts/dependencies/@openzeppelin/contracts/utils/introspection/IERC165.sol +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol) - -pragma solidity ^0.8.20; - -/** - * @dev Interface of the ERC-165 standard, as defined in the - * https://eips.ethereum.org/EIPS/eip-165[ERC]. - * - * Implementers can declare support of contract interfaces, which can then be - * queried by others ({ERC165Checker}). - * - * For an implementation, see {ERC165}. - */ -interface IERC165 { - /** - * @dev Returns true if this contract implements the interface defined by - * `interfaceId`. See the corresponding - * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section] - * to learn more about how these ids are created. - * - * This function call must use less than 30 000 gas. - */ - function supportsInterface(bytes4 interfaceId) external view returns (bool); -} diff --git a/cow-trader/contracts/dependencies/cowprotocol/contracts/src/contracts/interfaces/IERC20.sol b/cow-trader/contracts/dependencies/cowprotocol/contracts/src/contracts/interfaces/IERC20.sol deleted file mode 100644 index 610e9fd..0000000 --- a/cow-trader/contracts/dependencies/cowprotocol/contracts/src/contracts/interfaces/IERC20.sol +++ /dev/null @@ -1,98 +0,0 @@ -// SPDX-License-Identifier: MIT - -// Vendored from OpenZeppelin contracts with minor modifications: -// - Modified Solidity version -// - Formatted code -// - Added `name`, `symbol` and `decimals` function declarations -// - -pragma solidity >=0.7.6 <0.9.0; - -/** - * @dev Interface of the ERC20 standard as defined in the EIP. - */ -interface IERC20 { - /** - * @dev Returns the name of the token. - */ - function name() external view returns (string memory); - - /** - * @dev Returns the symbol of the token. - */ - function symbol() external view returns (string memory); - - /** - * @dev Returns the number of decimals the token uses. - */ - function decimals() external view returns (uint8); - - /** - * @dev Returns the amount of tokens in existence. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns the amount of tokens owned by `account`. - */ - function balanceOf(address account) external view returns (uint256); - - /** - * @dev Moves `amount` tokens from the caller's account to `recipient`. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transfer(address recipient, uint256 amount) external returns (bool); - - /** - * @dev Returns the remaining number of tokens that `spender` will be - * allowed to spend on behalf of `owner` through {transferFrom}. This is - * zero by default. - * - * This value changes when {approve} or {transferFrom} are called. - */ - function allowance(address owner, address spender) external view returns (uint256); - - /** - * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * IMPORTANT: Beware that changing an allowance with this method brings the risk - * that someone may use both the old and the new allowance by unfortunate - * transaction ordering. One possible solution to mitigate this race - * condition is to first reduce the spender's allowance to 0 and set the - * desired value afterwards: - * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - * - * Emits an {Approval} event. - */ - function approve(address spender, uint256 amount) external returns (bool); - - /** - * @dev Moves `amount` tokens from `sender` to `recipient` using the - * allowance mechanism. `amount` is then deducted from the caller's - * allowance. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); - - /** - * @dev Emitted when `value` tokens are moved from one account (`from`) to - * another (`to`). - * - * Note that `value` may be zero. - */ - event Transfer(address indexed from, address indexed to, uint256 value); - - /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` is the new allowance. - */ - event Approval(address indexed owner, address indexed spender, uint256 value); -} diff --git a/cow-trader/contracts/dependencies/cowprotocol/contracts/src/contracts/libraries/GPv2Order.sol b/cow-trader/contracts/dependencies/cowprotocol/contracts/src/contracts/libraries/GPv2Order.sol deleted file mode 100644 index a5fdcf5..0000000 --- a/cow-trader/contracts/dependencies/cowprotocol/contracts/src/contracts/libraries/GPv2Order.sol +++ /dev/null @@ -1,227 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-or-later -pragma solidity >=0.7.6 <0.9.0; - -import "../interfaces/IERC20.sol"; - -/// @title Gnosis Protocol v2 Order Library -/// @author Gnosis Developers -library GPv2Order { - /// @dev The complete data for a Gnosis Protocol order. This struct contains - /// all order parameters that are signed for submitting to GP. - struct Data { - IERC20 sellToken; - IERC20 buyToken; - address receiver; - uint256 sellAmount; - uint256 buyAmount; - uint32 validTo; - bytes32 appData; - uint256 feeAmount; - bytes32 kind; - bool partiallyFillable; - bytes32 sellTokenBalance; - bytes32 buyTokenBalance; - } - - /// @dev The order EIP-712 type hash for the [`GPv2Order.Data`] struct. - /// - /// This value is pre-computed from the following expression: - /// ``` - /// keccak256( - /// "Order(" + - /// "address sellToken," + - /// "address buyToken," + - /// "address receiver," + - /// "uint256 sellAmount," + - /// "uint256 buyAmount," + - /// "uint32 validTo," + - /// "bytes32 appData," + - /// "uint256 feeAmount," + - /// "string kind," + - /// "bool partiallyFillable," + - /// "string sellTokenBalance," + - /// "string buyTokenBalance" + - /// ")" - /// ) - /// ``` - bytes32 internal constant TYPE_HASH = hex"d5a25ba2e97094ad7d83dc28a6572da797d6b3e7fc6663bd93efb789fc17e489"; - - /// @dev The marker value for a sell order for computing the order struct - /// hash. This allows the EIP-712 compatible wallets to display a - /// descriptive string for the order kind (instead of 0 or 1). - /// - /// This value is pre-computed from the following expression: - /// ``` - /// keccak256("sell") - /// ``` - bytes32 internal constant KIND_SELL = hex"f3b277728b3fee749481eb3e0b3b48980dbbab78658fc419025cb16eee346775"; - - /// @dev The OrderKind marker value for a buy order for computing the order - /// struct hash. - /// - /// This value is pre-computed from the following expression: - /// ``` - /// keccak256("buy") - /// ``` - bytes32 internal constant KIND_BUY = hex"6ed88e868af0a1983e3886d5f3e95a2fafbd6c3450bc229e27342283dc429ccc"; - - /// @dev The TokenBalance marker value for using direct ERC20 balances for - /// computing the order struct hash. - /// - /// This value is pre-computed from the following expression: - /// ``` - /// keccak256("erc20") - /// ``` - bytes32 internal constant BALANCE_ERC20 = hex"5a28e9363bb942b639270062aa6bb295f434bcdfc42c97267bf003f272060dc9"; - - /// @dev The TokenBalance marker value for using Balancer Vault external - /// balances (in order to re-use Vault ERC20 approvals) for computing the - /// order struct hash. - /// - /// This value is pre-computed from the following expression: - /// ``` - /// keccak256("external") - /// ``` - bytes32 internal constant BALANCE_EXTERNAL = hex"abee3b73373acd583a130924aad6dc38cfdc44ba0555ba94ce2ff63980ea0632"; - - /// @dev The TokenBalance marker value for using Balancer Vault internal - /// balances for computing the order struct hash. - /// - /// This value is pre-computed from the following expression: - /// ``` - /// keccak256("internal") - /// ``` - bytes32 internal constant BALANCE_INTERNAL = hex"4ac99ace14ee0a5ef932dc609df0943ab7ac16b7583634612f8dc35a4289a6ce"; - - /// @dev Marker address used to indicate that the receiver of the trade - /// proceeds should the owner of the order. - /// - /// This is chosen to be `address(0)` for gas efficiency as it is expected - /// to be the most common case. - address internal constant RECEIVER_SAME_AS_OWNER = address(0); - - /// @dev The byte length of an order unique identifier. - uint256 internal constant UID_LENGTH = 56; - - /// @dev Returns the actual receiver for an order. This function checks - /// whether or not the [`receiver`] field uses the marker value to indicate - /// it is the same as the order owner. - /// - /// @return receiver The actual receiver of trade proceeds. - function actualReceiver(Data memory order, address owner) internal pure returns (address receiver) { - if (order.receiver == RECEIVER_SAME_AS_OWNER) { - receiver = owner; - } else { - receiver = order.receiver; - } - } - - /// @dev Return the EIP-712 signing hash for the specified order. - /// - /// @param order The order to compute the EIP-712 signing hash for. - /// @param domainSeparator The EIP-712 domain separator to use. - /// @return orderDigest The 32 byte EIP-712 struct hash. - function hash(Data memory order, bytes32 domainSeparator) internal pure returns (bytes32 orderDigest) { - bytes32 structHash; - - // NOTE: Compute the EIP-712 order struct hash in place. As suggested - // in the EIP proposal, noting that the order struct has 12 fields, and - // prefixing the type hash `(1 + 12) * 32 = 416` bytes to hash. - // - // solhint-disable-next-line no-inline-assembly - assembly { - let dataStart := sub(order, 32) - let temp := mload(dataStart) - mstore(dataStart, TYPE_HASH) - structHash := keccak256(dataStart, 416) - mstore(dataStart, temp) - } - - // NOTE: Now that we have the struct hash, compute the EIP-712 signing - // hash using scratch memory past the free memory pointer. The signing - // hash is computed from `"\x19\x01" || domainSeparator || structHash`. - // - // - // solhint-disable-next-line no-inline-assembly - assembly { - let freeMemoryPointer := mload(0x40) - mstore(freeMemoryPointer, "\x19\x01") - mstore(add(freeMemoryPointer, 2), domainSeparator) - mstore(add(freeMemoryPointer, 34), structHash) - orderDigest := keccak256(freeMemoryPointer, 66) - } - } - - /// @dev Packs order UID parameters into the specified memory location. The - /// result is equivalent to `abi.encodePacked(...)` with the difference that - /// it allows re-using the memory for packing the order UID. - /// - /// This function reverts if the order UID buffer is not the correct size. - /// - /// @param orderUid The buffer pack the order UID parameters into. - /// @param orderDigest The EIP-712 struct digest derived from the order - /// parameters. - /// @param owner The address of the user who owns this order. - /// @param validTo The epoch time at which the order will stop being valid. - function packOrderUidParams(bytes memory orderUid, bytes32 orderDigest, address owner, uint32 validTo) - internal - pure - { - require(orderUid.length == UID_LENGTH, "GPv2: uid buffer overflow"); - - // NOTE: Write the order UID to the allocated memory buffer. The order - // parameters are written to memory in **reverse order** as memory - // operations write 32-bytes at a time and we want to use a packed - // encoding. This means, for example, that after writing the value of - // `owner` to bytes `20:52`, writing the `orderDigest` to bytes `0:32` - // will **overwrite** bytes `20:32`. This is desirable as addresses are - // only 20 bytes and `20:32` should be `0`s: - // - // | 1111111111222222222233333333334444444444555555 - // byte | 01234567890123456789012345678901234567890123456789012345 - // -------+--------------------------------------------------------- - // field | [.........orderDigest..........][......owner.......][vT] - // -------+--------------------------------------------------------- - // mstore | [000000000000000000000000000.vT] - // | [00000000000.......owner.......] - // | [.........orderDigest..........] - // - // Additionally, since Solidity `bytes memory` are length prefixed, - // 32 needs to be added to all the offsets. - // - // solhint-disable-next-line no-inline-assembly - assembly { - mstore(add(orderUid, 56), validTo) - mstore(add(orderUid, 52), owner) - mstore(add(orderUid, 32), orderDigest) - } - } - - /// @dev Extracts specific order information from the standardized unique - /// order id of the protocol. - /// - /// @param orderUid The unique identifier used to represent an order in - /// the protocol. This uid is the packed concatenation of the order digest, - /// the validTo order parameter and the address of the user who created the - /// order. It is used by the user to interface with the contract directly, - /// and not by calls that are triggered by the solvers. - /// @return orderDigest The EIP-712 signing digest derived from the order - /// parameters. - /// @return owner The address of the user who owns this order. - /// @return validTo The epoch time at which the order will stop being valid. - function extractOrderUidParams(bytes calldata orderUid) - internal - pure - returns (bytes32 orderDigest, address owner, uint32 validTo) - { - require(orderUid.length == UID_LENGTH, "GPv2: invalid uid"); - - // Use assembly to efficiently decode packed calldata. - // solhint-disable-next-line no-inline-assembly - assembly { - orderDigest := calldataload(orderUid.offset) - owner := shr(96, calldataload(add(orderUid.offset, 32))) - validTo := shr(224, calldataload(add(orderUid.offset, 52))) - } - } -} diff --git a/cow-trader/contracts/interfaces/ITokenAllowlist.sol b/cow-trader/contracts/interfaces/ITokenAllowlist.sol deleted file mode 100644 index 5a08fd1..0000000 --- a/cow-trader/contracts/interfaces/ITokenAllowlist.sol +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.25; - -interface ITokenAllowlist { - function isOrderAllowed(address sellToken, address buyToken) external view returns (bool); -} diff --git a/cow-trader/tests/test_allowlist.py b/cow-trader/tests/test_allowlist.py deleted file mode 100644 index 1a5d367..0000000 --- a/cow-trader/tests/test_allowlist.py +++ /dev/null @@ -1,105 +0,0 @@ -import ape -import pytest - - -# Gnosis chain token addresses -class TokenAddresses: - GNO = "0x9C58BAcC331c9aa871AFD802DB6379a98e80CEdb" - COW = "0x177127622c4A00F3d409B75571e12cB3c8973d3c" - WETH = "0x6A023CCd1ff6F2045C3309768eAd9E68F978f6e1" - SAFE = "0x5aFE3855358E112B5647B952709E6165e1c1eEEe" - - -@pytest.fixture -def tokens(): - return TokenAddresses() - - -@pytest.fixture -def token_batch(tokens): - return [tokens.GNO, tokens.COW, tokens.WETH, tokens.SAFE] - - -@pytest.fixture -def owner(accounts): - return accounts[0] - - -@pytest.fixture -def not_owner(accounts): - return accounts[1] - - -@pytest.fixture -def allowlist_contract(owner, project): - return owner.deploy(project.TokenAllowlist) - - -@pytest.fixture -def populated_allowlist(allowlist_contract, owner, token_batch): - allowlist_contract.addTokensBatch(token_batch, sender=owner) - return allowlist_contract - - -def test_add_token(allowlist_contract, tokens, owner, not_owner): - allowlist_contract.addToken(tokens.GNO, sender=owner) - allowed = allowlist_contract.isAllowed(tokens.GNO) - assert allowed is True - - with ape.reverts(): - allowlist_contract.addToken(tokens.GNO, sender=not_owner) - - -def test_token_not_allowed(allowlist_contract, tokens, owner): - allowlist_contract.addToken(tokens.GNO, sender=owner) - allowed = allowlist_contract.isAllowed(tokens.COW) - assert allowed is False - - -def test_add_batch_tokens(allowlist_contract, token_batch, owner, not_owner): - allowlist_contract.addTokensBatch(token_batch, sender=owner) - for token in token_batch: - allowed = allowlist_contract.isAllowed(token) - assert allowed is True - - with ape.reverts(): - allowlist_contract.addTokensBatch(token_batch, sender=not_owner) - - -def test_allowed_tokens(allowlist_contract, token_batch, owner): - allowlist_contract.addTokensBatch(token_batch, sender=owner) - allowed_tokens = allowlist_contract.allowedTokens() - assert allowed_tokens == token_batch - - -def test_remove_token(populated_allowlist, tokens, owner, not_owner): - populated_allowlist.removeToken(tokens.GNO, sender=owner) - allowed = populated_allowlist.isAllowed(tokens.GNO) - assert allowed is False - - expected_tokens = [tokens.COW, tokens.WETH, tokens.SAFE] - allowed_tokens = populated_allowlist.allowedTokens() - assert set(allowed_tokens) == set(expected_tokens) - - with ape.reverts(): - populated_allowlist.removeToken(tokens.GNO, sender=not_owner) - - -def test_remove_batch_tokens(populated_allowlist, tokens, owner, not_owner): - tokens_to_remove = [tokens.GNO, tokens.WETH] - populated_allowlist.removeTokensBatch(tokens_to_remove, sender=owner) - - for token in tokens_to_remove: - allowed = populated_allowlist.isAllowed(token) - assert allowed is False - - remaining_tokens = [tokens.COW, tokens.SAFE] - for token in remaining_tokens: - allowed = populated_allowlist.isAllowed(token) - assert allowed is True - - allowed_tokens = populated_allowlist.allowedTokens() - assert set(allowed_tokens) == set(remaining_tokens) - - with ape.reverts(): - populated_allowlist.removeTokensBatch(tokens_to_remove, sender=not_owner) From fb03bcdb272ceb1942340cc0eec7317211cadd48 Mon Sep 17 00:00:00 2001 From: lumoswiz Date: Thu, 6 Feb 2025 23:41:13 +0100 Subject: [PATCH 19/37] chore: add solady depedency and update remappings --- smart-contract-infra/bun.lockb | Bin 30319 -> 30659 bytes smart-contract-infra/package.json | 3 ++- smart-contract-infra/remappings.txt | 4 +++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/smart-contract-infra/bun.lockb b/smart-contract-infra/bun.lockb index acfc41184f9155c385533d8be7ae1e6023c5148a..1b52869ff4662a0362dd04cc062eed9e982e5d98 100755 GIT binary patch delta 4835 zcmeHLdvH|M8NcU}8*-B@Ng$A1HX$TS62fM8v)Sx!k|nuBn z)dY$&0ZSD>vN&`z8Vw7!te6yK<=?W$=fosQLx;Lwg#Xodd1ySt<9sQ>go zr)TE7-~P_|&Ue1YdG044i*uidhn!{C*Is>dqkHGAxBtc{S@-0RPyQitWcY)E+m77+ z>hd+eeX`~m&s~yPn)e3_X6$W>f!H@&k_P%BJ>m5`B&i2gHej3B|FF~TlO-t)$Ujb! z(!tAsE#P@^o(w((SVu3`GPwV8PJgc=NjU)Ft=oEftG4xR1ZE|tlO;(7KLwr&9vLr5 zS>Vm!`QRIRdn3J$4cphJ#N->|?MdiVg!XHw=YE&LSZ9*{=}lxx2`WwO@(XKLE_bzYEUo;mvoN@$QAwlYl$HIcT?p`?fjm=-spo*c_)j z+;b}=-<~K*P(5p)JKX2Er6;^`V^3r)Yj_mBdEkBEY_A3_+;JnW6~E3Y6|wUn<06a`tHZJIDZTAD6ircj!$lq1L0l%En15sISrBx&io zT%uBIdcF7_4X5kMdAM$>kTX+hs-+&ohb+2!A3{9`hK-|enqT=HBOXW1LBwt{4GvrU z>RO;$Q>qZ?5Kui3LIdPQZ#=}0>V zCzYkdN(yD_VvvS2b@e&4a1s6lzD8chy}`$@~z z)wh9{Lt1cO**1|5X4i{0ie~HLC~1>*bqurbW(bVu1S`duYRCuT*nwXasE09#)lncG z)Knw>NTDgZG85BrJ`b(lgI}xzt=WF@0%@%TVbnNm}66r%&u{c z_(*mT7v`+3oB;}RmdC(mVV(r?#^8T&?#I@00k4#>vZNXlel_O~n001hF4MjFu70jX zCd~4`a5j5xTn}@$W_}!hnRET3cs+Q(>HlI>upvggfjRrVERMg4%aCaTasTGHJaaZE z7{|=HeRZ6N;`Jduv$${tD{5na!kjx`CP_(DmzPt>C=BBA-<<3IKSekS-#C@N~!R{gOl4XO*?o?$(8Dhm)j0iKW#hMAGvj~Z)4l*J>3r-aeQaNHs?Q| zyzk)R@RHt3DdRg{-@9z{ch?+DnqPkSgUz(BAeR~o3^9&|3xc$8x`jRmQfSfiAbkdO za=L*{>MGFjLJM^i8bYNLg+W?fWFbqDA<`&R6r>cJg`NenkZKFkX`n4OLuAkypzawK z%Aa9~EZQ_9NV&xpdJSk2X~jW04|H#_AtuvHK-)_!>{N5vYJ_%7XME(BU#e6w*gPL$fT@ILi<=8lDCJ z?C=k$m=@XLAJ9p=Axh~g(D8EkS8j+hI#CY)9PrO!2s?!w@XrbVfE=Vc;UCZzry*Q) z2B_Nw|6GP}(d3S@S%?KA zh4pX9;?t4KbAFgX7i!&U;sWogDASwi2g~bthrE6xR=9R4h$lSnnq0n&^I}!z_RTW9 z=kwXZvN(;+e#_%~^??;MtEpZj)261m9c5_bGZUw?lndhRw*%A(>H=|}6`+-%7SJjX zpAD>(-x3X=n?QUF)`9pub%Izzw!VTP4%y#yPb#Rzj5BKL2lq11WsQ_^V zI1&y%aycvoAUlYS<*=86N!4BA&BKUEx2Bp23Wy05T_{zG!--rgcq5_ z`JWEr!?g%B12hxFw+=7HHwWX0x^Aa!G#0y9k6;8L4 zt+EN4&{CO~c)UKC`o7HST+4W3Q?}@=a++#(Q`RaC+mC&f_6@oQscV%-wuzAgt9(Mn zoAcFRt~K!lz5A(#-AiX5ZIMN%+ga(v4AALDQ1d39uK7h{winc*Gidk422DI}TcTt3 zi3P^pXsL+xY^TQ6nl>Cm#9O`F z+x57s{Nf{!_QtfGru>j*Jqu}e{z&max&8hPYqLdtU6r%O>6IF&Dde#x-u8jL$KJ0j zym}V>D-lDC7NH{{&6{}ZU)lQTuI$vuZE*8K=W!Y{Tj@%~@xdhwX{^119Uj1igi{kB}|*LQt@&bDdd zU3w4SW%OyA$D8<*AUarby}oTwg)(=i3(Zq>EqTQWEbiDH?lVeNN d<#5G*ILrk~4QmSMrLG*ZccqWq(RFRgzX9Ozd=dZv delta 4541 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 Date: Thu, 6 Feb 2025 23:42:26 +0100 Subject: [PATCH 20/37] chore: simplify .gitignore and include Foundry artifacts --- .gitignore | 70 +++++++++++++++++++++++++----------------------------- 1 file changed, 33 insertions(+), 37 deletions(-) diff --git a/.gitignore b/.gitignore index 97b02e7..2e7d66d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,48 +1,44 @@ -# Silverback -__pycache__/ -*.py[oc] +# Directories +.build/ +.cache +.db/ +.idea/ +.npm +.nyc_output/ +.pytest_cache/ +.silverback* +.venv +.vscode/ +.yarn/ +abi/ build/ +cache/ +coverage/ dist/ +node_modules/ +out/ wheels/ -*.egg-info -.venv -.pytest_cache/ -.build/ -.silverback* -.ruff* -# Bun -node_modules/ -dist/ +# Files +*.egg-info +*.env +*.lcov +*.log +*.py[oc] *.tsbuildinfo -coverage/ -.npm +.DS_Store .eslintcache -.yarn/ .pnp.* -.cache - -# Logs -logs/ -*.log +.ruff* +lcov.info npm-debug.log* +package-lock.json +pnpm-lock.yaml yarn-debug.log* yarn-error.log* +yarn.lock -# Environment -.env -.env.* - -# IDE/OS -.vscode/ -.idea/ -.DS_Store - -# Project specific -.db/ -abi/ - -# Test coverage -coverage/ -.nyc_output/ -*.lcov \ No newline at end of file +# Broadcasts +!broadcast +broadcast/* +broadcast/*/31337/ \ No newline at end of file From 4a7f1f9fce9a7c33983d548f439d7e5bedc57202 Mon Sep 17 00:00:00 2001 From: lumoswiz Date: Thu, 6 Feb 2025 23:42:45 +0100 Subject: [PATCH 21/37] feat: add TokenAllowlist to Foundry project --- smart-contract-infra/src/TokenAllowlist.sol | 47 +++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 smart-contract-infra/src/TokenAllowlist.sol diff --git a/smart-contract-infra/src/TokenAllowlist.sol b/smart-contract-infra/src/TokenAllowlist.sol new file mode 100644 index 0000000..9307f7d --- /dev/null +++ b/smart-contract-infra/src/TokenAllowlist.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.28; + +import { Ownable } from "solady/src/auth/Ownable.sol"; +import { EnumerableSetLib } from "solady/src/utils/EnumerableSetLib.sol"; + +contract TokenAllowlist is Ownable { + using EnumerableSetLib for EnumerableSetLib.AddressSet; + + EnumerableSetLib.AddressSet internal allowlist; + + constructor() { + _initializeOwner(msg.sender); + } + + function addToken(address token) external onlyOwner { + allowlist.add(token); + } + + function addTokensBatch(address[] memory tokens) external onlyOwner { + for (uint256 i; i < tokens.length; ++i) { + allowlist.add(tokens[i]); + } + } + + function removeToken(address token) external onlyOwner { + allowlist.remove(token); + } + + function removeTokensBatch(address[] memory tokens) external onlyOwner { + for (uint256 i; i < tokens.length; ++i) { + allowlist.remove(tokens[i]); + } + } + + function allowedTokens() external view returns (address[] memory) { + return allowlist.values(); + } + + function isAllowed(address token) public view returns (bool) { + return allowlist.contains(token); + } + + function isOrderAllowed(address sellToken, address buyToken) external view returns (bool) { + return isAllowed(sellToken) && isAllowed(buyToken); + } +} From 55d24ebf6d1dc7642cb6fc28e81c7822371364b3 Mon Sep 17 00:00:00 2001 From: lumoswiz Date: Thu, 6 Feb 2025 23:43:20 +0100 Subject: [PATCH 22/37] feat: add interface for token allowlist --- smart-contract-infra/src/interfaces/ITokenAllowlist.sol | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 smart-contract-infra/src/interfaces/ITokenAllowlist.sol diff --git a/smart-contract-infra/src/interfaces/ITokenAllowlist.sol b/smart-contract-infra/src/interfaces/ITokenAllowlist.sol new file mode 100644 index 0000000..6db9348 --- /dev/null +++ b/smart-contract-infra/src/interfaces/ITokenAllowlist.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.28; + +interface ITokenAllowlist { + function isOrderAllowed(address sellToken, address buyToken) external view returns (bool); +} From 6c572bc14cb8a90955cb55514c11018f64349440 Mon Sep 17 00:00:00 2001 From: lumoswiz Date: Fri, 7 Feb 2025 00:03:58 +0100 Subject: [PATCH 23/37] chore: update dependencies and remappings --- smart-contract-infra/bun.lockb | Bin 30659 -> 31115 bytes smart-contract-infra/package.json | 1 + smart-contract-infra/remappings.txt | 16 +++++++--------- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/smart-contract-infra/bun.lockb b/smart-contract-infra/bun.lockb index 1b52869ff4662a0362dd04cc062eed9e982e5d98..47ff29e12bec68c81decf1894b7243a1f940c6ac 100755 GIT binary patch delta 5027 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 R3C*YH+x|6Szxjdm{{Yj+vM~Sv delta 4924 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 diff --git a/smart-contract-infra/package.json b/smart-contract-infra/package.json index 9decc8f..3381606 100644 --- a/smart-contract-infra/package.json +++ b/smart-contract-infra/package.json @@ -6,6 +6,7 @@ "@gnosis-guild/zodiac": "4.0.3", "@gnosis.pm/safe-contracts": "1.3.0", "@openzeppelin/contracts-upgradeable": "5.2.0", + "cowprotocol/contracts": "github:cowprotocol/contracts#9c1984b", "solady": "0.1.6" }, "devDependencies": { diff --git a/smart-contract-infra/remappings.txt b/smart-contract-infra/remappings.txt index f6d9784..5cc0e5c 100644 --- a/smart-contract-infra/remappings.txt +++ b/smart-contract-infra/remappings.txt @@ -1,9 +1,7 @@ -forge-std/=lib/forge-std/src/ -zodiac/=lib/zodiac/contracts/ -@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/ -@openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/ -openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/ -openzeppelin-contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/ -@gnosis.pm/safe-contracts/=lib/safe-smart-account/ -@gnosis-guild/=node_modules/@gnosis-guild/ -solady/=node_modules/solady/ \ No newline at end of file +forge-std/=node_modules/forge-std/src/ +@openzeppelin/contracts-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable/ +@openzeppelin/contracts/=node_modules/@openzeppelin/contracts/ +@gnosis.pm/safe-contracts/=node_modules/@gnosis.pm/safe-contracts/ +@gnosis-guild/zodiac/=node_modules/@gnosis-guild/zodiac/ +solady/=node_modules/solady/ +cowprotocol/=node_modules/cowprotocol/ \ No newline at end of file From 5edbd301bea9f4bca6e28bb721cf484aae168d3f Mon Sep 17 00:00:00 2001 From: lumoswiz Date: Fri, 7 Feb 2025 00:08:42 +0100 Subject: [PATCH 24/37] feat: add trading module --- smart-contract-infra/src/TradingModule.sol | 66 ++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 smart-contract-infra/src/TradingModule.sol diff --git a/smart-contract-infra/src/TradingModule.sol b/smart-contract-infra/src/TradingModule.sol new file mode 100644 index 0000000..01d71ea --- /dev/null +++ b/smart-contract-infra/src/TradingModule.sol @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.28; + +import { Enum } from "@gnosis.pm/safe-contracts/contracts/common/Enum.sol"; +import { Module } from "@gnosis-guild/zodiac/contracts/core/Module.sol"; +import { GPv2Order } from "cowprotocol/contracts/src/contracts/libraries/GPv2Order.sol"; +import { GPv2Signing } from "cowprotocol/contracts/src/contracts/mixins/GPv2Signing.sol"; +import { ITokenAllowlist } from "./interfaces/ITokenAllowlist.sol"; + +abstract contract TradingModule is Module { + using GPv2Order for bytes; + + event SetOrderTradeable( + bytes indexed orderUid, + address indexed sellToken, + address indexed buyToken, + uint256 sellAmount, + uint256 buyAmount + ); + + error InvalidOrderUID(); + error OrderNotAllowed(); + + address public constant GPV2_SETTLEMENT_ADDRESS = 0x9008D19f58AAbD9eD0D60971565AA8510560ab41; + + ITokenAllowlist internal allowlist; + bytes32 internal domainSeparator; + + constructor(address _owner, address _avatar, address _target, address _tokenAllowlist) { + bytes memory initParams = abi.encode(_owner, _avatar, _target, _tokenAllowlist); + setUp(initParams); + } + + function setUp(bytes memory initParams) public override initializer { + (address _owner, address _avatar, address _target, address _tokenAllowlist) = + abi.decode(initParams, (address, address, address, address)); + + require(_avatar != address(0)); + require(_target != address(0)); + + __Ownable_init(msg.sender); + + setAvatar(_avatar); + setTarget(_target); + allowlist = ITokenAllowlist(_tokenAllowlist); + domainSeparator = GPv2Signing(GPV2_SETTLEMENT_ADDRESS).domainSeparator(); + + transferOwnership(_owner); + } + + function setOrderTradeable(bytes memory orderUid, GPv2Order.Data memory order) external { + bytes memory uid; + uid.packOrderUidParams(GPv2Order.hash(order, domainSeparator), owner(), order.validTo); + + require(keccak256(orderUid) == keccak256(uid), InvalidOrderUID()); + + require(allowlist.isOrderAllowed(address(order.sellToken), address(order.buyToken)), OrderNotAllowed()); + + bytes memory data = abi.encodeWithSignature("setPreSignature(bytes,bool)", orderUid, true); + require(exec(GPV2_SETTLEMENT_ADDRESS, 0, data, Enum.Operation.Call)); + + emit SetOrderTradeable( + orderUid, address(order.sellToken), address(order.buyToken), order.sellAmount, order.buyAmount + ); + } +} From f069aed3a94f72bd29d58a1657d1653dd54a567b Mon Sep 17 00:00:00 2001 From: lumoswiz Date: Fri, 7 Feb 2025 00:15:00 +0100 Subject: [PATCH 25/37] feat: add guardable to trading module --- smart-contract-infra/src/TradingModule.sol | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/smart-contract-infra/src/TradingModule.sol b/smart-contract-infra/src/TradingModule.sol index 01d71ea..61f8aa8 100644 --- a/smart-contract-infra/src/TradingModule.sol +++ b/smart-contract-infra/src/TradingModule.sol @@ -3,11 +3,12 @@ pragma solidity 0.8.28; import { Enum } from "@gnosis.pm/safe-contracts/contracts/common/Enum.sol"; import { Module } from "@gnosis-guild/zodiac/contracts/core/Module.sol"; +import { Guardable } from "@gnosis-guild/zodiac/contracts/guard/Guardable.sol"; import { GPv2Order } from "cowprotocol/contracts/src/contracts/libraries/GPv2Order.sol"; import { GPv2Signing } from "cowprotocol/contracts/src/contracts/mixins/GPv2Signing.sol"; import { ITokenAllowlist } from "./interfaces/ITokenAllowlist.sol"; -abstract contract TradingModule is Module { +abstract contract TradingModule is Module, Guardable { using GPv2Order for bytes; event SetOrderTradeable( @@ -26,14 +27,14 @@ abstract contract TradingModule is Module { ITokenAllowlist internal allowlist; bytes32 internal domainSeparator; - constructor(address _owner, address _avatar, address _target, address _tokenAllowlist) { - bytes memory initParams = abi.encode(_owner, _avatar, _target, _tokenAllowlist); + constructor(address _owner, address _avatar, address _target, address _guard, address _tokenAllowlist) { + bytes memory initParams = abi.encode(_owner, _avatar, _target, _guard, _tokenAllowlist); setUp(initParams); } function setUp(bytes memory initParams) public override initializer { - (address _owner, address _avatar, address _target, address _tokenAllowlist) = - abi.decode(initParams, (address, address, address, address)); + (address _owner, address _avatar, address _target, address _guard, address _tokenAllowlist) = + abi.decode(initParams, (address, address, address, address, address)); require(_avatar != address(0)); require(_target != address(0)); @@ -42,6 +43,8 @@ abstract contract TradingModule is Module { setAvatar(_avatar); setTarget(_target); + guard = _guard; + allowlist = ITokenAllowlist(_tokenAllowlist); domainSeparator = GPv2Signing(GPV2_SETTLEMENT_ADDRESS).domainSeparator(); From 76387c6741da593cadfbf810c7aae383a8adf30a Mon Sep 17 00:00:00 2001 From: lumoswiz Date: Fri, 7 Feb 2025 00:18:14 +0100 Subject: [PATCH 26/37] feat: add guard to exec function --- smart-contract-infra/src/TradingModule.sol | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/smart-contract-infra/src/TradingModule.sol b/smart-contract-infra/src/TradingModule.sol index 61f8aa8..8830b0e 100644 --- a/smart-contract-infra/src/TradingModule.sol +++ b/smart-contract-infra/src/TradingModule.sol @@ -6,6 +6,9 @@ import { Module } from "@gnosis-guild/zodiac/contracts/core/Module.sol"; import { Guardable } from "@gnosis-guild/zodiac/contracts/guard/Guardable.sol"; import { GPv2Order } from "cowprotocol/contracts/src/contracts/libraries/GPv2Order.sol"; import { GPv2Signing } from "cowprotocol/contracts/src/contracts/mixins/GPv2Signing.sol"; + +import { IAvatar } from "@gnosis-guild/zodiac/contracts/interfaces/IAvatar.sol"; +import { IGuard } from "@gnosis-guild/zodiac/contracts/interfaces/IGuard.sol"; import { ITokenAllowlist } from "./interfaces/ITokenAllowlist.sol"; abstract contract TradingModule is Module, Guardable { @@ -66,4 +69,23 @@ abstract contract TradingModule is Module, Guardable { orderUid, address(order.sellToken), address(order.buyToken), order.sellAmount, order.buyAmount ); } + + function exec( + address to, + uint256 value, + bytes memory data, + Enum.Operation operation + ) + internal + override + returns (bool) + { + address currentGuard = guard; + if (currentGuard != address(0)) { + IGuard(currentGuard).checkTransaction( + to, value, data, operation, 0, 0, 0, address(0), payable(0), "", msg.sender + ); + } + return IAvatar(target).execTransactionFromModule(to, value, data, operation); + } } From 024b33b783070a8f4a1ef129f870f9be0b848c04 Mon Sep 17 00:00:00 2001 From: lumoswiz Date: Fri, 7 Feb 2025 00:22:45 +0100 Subject: [PATCH 27/37] feat: initial CoWSwapGuard impl --- smart-contract-infra/src/CoWSwapGuard.sol | 39 +++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 smart-contract-infra/src/CoWSwapGuard.sol diff --git a/smart-contract-infra/src/CoWSwapGuard.sol b/smart-contract-infra/src/CoWSwapGuard.sol new file mode 100644 index 0000000..242776a --- /dev/null +++ b/smart-contract-infra/src/CoWSwapGuard.sol @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.28; + +import { Enum } from "@gnosis.pm/safe-contracts/contracts/common/Enum.sol"; +import { BaseGuard } from "@gnosis-guild/zodiac/contracts/guard/BaseGuard.sol"; + +contract CoWSwapGuard is BaseGuard { + error InvalidAddress(); + error InvalidSelector(); + + address internal constant GPV2_SETTLEMENT_ADDRESS = 0x9008D19f58AAbD9eD0D60971565AA8510560ab41; + + bytes4 internal constant SET_PRE_SIGNATURE_SELECTOR = 0xec6cb13f; + + function checkTransaction( + address to, + uint256, + bytes memory data, + Enum.Operation, + uint256, + uint256, + uint256, + address, + address payable, + bytes memory, + address + ) + external + pure + override + { + require(to == GPV2_SETTLEMENT_ADDRESS, InvalidAddress()); + + (bytes4 selector,,) = abi.decode(data, (bytes4, bytes, bool)); + require(selector == SET_PRE_SIGNATURE_SELECTOR, InvalidSelector()); + } + + function checkAfterExecution(bytes32 txHash, bool success) external override { } +} From 0c91393362e557daa1b19e8b417ada2b372c9fc6 Mon Sep 17 00:00:00 2001 From: lumoswiz Date: Fri, 7 Feb 2025 15:01:01 +0100 Subject: [PATCH 28/37] chore: add foundry artifacts to prettier ignore --- smart-contract-infra/.prettierignore | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/smart-contract-infra/.prettierignore b/smart-contract-infra/.prettierignore index 3a89310..2a77960 100644 --- a/smart-contract-infra/.prettierignore +++ b/smart-contract-infra/.prettierignore @@ -1,5 +1,11 @@ -node_modules +# directories +broadcast +cache coverage +node_modules +out + +# files *.env *.log .DS_Store @@ -7,5 +13,5 @@ coverage bun.lockb lcov.info package-lock.json -yarn.lock -cow-trader/ \ No newline at end of file +pnpm-lock.yaml +yarn.lock \ No newline at end of file From 6bcaa87d2fe227c9768de32f43c659f0409b76aa Mon Sep 17 00:00:00 2001 From: lumoswiz Date: Fri, 7 Feb 2025 15:01:30 +0100 Subject: [PATCH 29/37] chore: ignore custom errors, reason strings and no empty code blocks --- smart-contract-infra/.solhint.json | 48 +++++++++++++----------------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/smart-contract-infra/.solhint.json b/smart-contract-infra/.solhint.json index 02948bf..1603dc4 100644 --- a/smart-contract-infra/.solhint.json +++ b/smart-contract-infra/.solhint.json @@ -1,28 +1,22 @@ { - "extends": "solhint:recommended", - "rules": { - "code-complexity": [ - "error", - 8 - ], - "compiler-version": [ - "error", - ">=0.8.28" - ], - "func-name-mixedcase": "off", - "func-visibility": [ - "error", - { - "ignoreConstructors": true - } - ], - "max-line-length": [ - "error", - 120 - ], - "named-parameters-mapping": "warn", - "no-console": "off", - "not-rely-on-time": "off", - "one-contract-per-file": "off" - } -} \ No newline at end of file + "extends": "solhint:recommended", + "rules": { + "code-complexity": ["error", 8], + "compiler-version": ["error", ">=0.8.28"], + "func-name-mixedcase": "off", + "func-visibility": [ + "error", + { + "ignoreConstructors": true + } + ], + "max-line-length": ["error", 120], + "named-parameters-mapping": "warn", + "no-console": "off", + "not-rely-on-time": "off", + "one-contract-per-file": "off", + "custom-errors": "off", + "reason-string": "off", + "no-empty-blocks": "off" + } +} From e9d6aedd0a497e3b43cf9eb86e9511f52e220e9f Mon Sep 17 00:00:00 2001 From: lumoswiz Date: Fri, 7 Feb 2025 15:02:03 +0100 Subject: [PATCH 30/37] chore: add ankr rpcs --- smart-contract-infra/foundry.toml | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/smart-contract-infra/foundry.toml b/smart-contract-infra/foundry.toml index 89e0827..e8e61b4 100644 --- a/smart-contract-infra/foundry.toml +++ b/smart-contract-infra/foundry.toml @@ -1,10 +1,7 @@ [profile.default] auto_detect_solc = false block_timestamp = 1_738_368_000 # Feb 1, 2025 at 00:00 GMT -bytecode_hash = "none" -evm_version = "shanghai" fuzz = { runs = 1_000 } -gas_reports = ["*"] optimizer = true optimizer_runs = 10_000 out = "out" @@ -31,9 +28,9 @@ tab_width = 4 wrap_comments = true [rpc_endpoints] -arbitrum = "https://arbitrum-one-rpc.publicnode.com" -base = "https://mainnet.base.org" -gnosis_chain = "https://rpc.gnosischain.com" +arbitrum = "https://rpc.ankr.com/arbitrum" +base = "https://rpc.ankr.com/base" +gnosis = "https://rpc.ankr.com/gnosis" localhost = "http://localhost:8545" -mainnet = "https://eth-mainnet.g.alchemy.com/v2/${API_KEY_ALCHEMY}" -sepolia = "https://ethereum-sepolia-rpc.publicnode.com" +mainnet = "https://rpc.ankr.com/eth" +sepolia = "https://rpc2.sepolia.org" From 793f2241d844bfad960864933b1acff9fce02acb Mon Sep 17 00:00:00 2001 From: lumoswiz Date: Fri, 7 Feb 2025 15:03:25 +0100 Subject: [PATCH 31/37] chore: remove template readme content --- smart-contract-infra/README.md | 67 +--------------------------------- 1 file changed, 1 insertion(+), 66 deletions(-) diff --git a/smart-contract-infra/README.md b/smart-contract-infra/README.md index 9265b45..227cef0 100644 --- a/smart-contract-infra/README.md +++ b/smart-contract-infra/README.md @@ -1,66 +1 @@ -## Foundry - -**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.** - -Foundry consists of: - -- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools). -- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data. -- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network. -- **Chisel**: Fast, utilitarian, and verbose solidity REPL. - -## Documentation - -https://book.getfoundry.sh/ - -## Usage - -### Build - -```shell -$ forge build -``` - -### Test - -```shell -$ forge test -``` - -### Format - -```shell -$ forge fmt -``` - -### Gas Snapshots - -```shell -$ forge snapshot -``` - -### Anvil - -```shell -$ anvil -``` - -### Deploy - -```shell -$ forge script script/Counter.s.sol:CounterScript --rpc-url --private-key -``` - -### Cast - -```shell -$ cast -``` - -### Help - -```shell -$ forge --help -$ anvil --help -$ cast --help -``` +## Smart Contract Infrastructure From 72449c15658a8633b92c79327905af2bdb0f3621 Mon Sep 17 00:00:00 2001 From: lumoswiz Date: Fri, 7 Feb 2025 18:18:40 +0100 Subject: [PATCH 32/37] refactor: restructure GitHub Actions for monorepo Remove trader-build and trader-test jobs Add Foundry-based contract testing pipeline Simplify workflow to lint/build/test smart contracts Keep Python linting for cow-trader --- .github/workflows/ci.yml | 127 +++++++++++++++------------------------ 1 file changed, 50 insertions(+), 77 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 071131b..96e6991 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,30 +8,6 @@ on: - 'main' jobs: - lint: - name: 'Smart Contract Infra Lint' - runs-on: ubuntu-latest - defaults: - run: - working-directory: ./smart-contract-infra - steps: - - name: 'Checkout the repo' - uses: actions/checkout@v4 - - - name: 'Install Bun' - uses: oven-sh/setup-bun@v1 - - - name: 'Install the node.js dependencies' - run: bun install - - - name: 'Lint the code' - run: bun run lint - - - name: 'Add lint summary' - run: | - echo "## Lint result" >> $GITHUB_STEP_SUMMARY - echo "✅ Passed" >> $GITHUB_STEP_SUMMARY - trader-lint: name: 'CoW Trader Lint' runs-on: 'ubuntu-latest' @@ -63,85 +39,82 @@ jobs: echo "## Lint result" >> $GITHUB_STEP_SUMMARY echo "✅ Passed" >> $GITHUB_STEP_SUMMARY - trader-build: - name: 'CoW Trader Build' + contracts-lint: + name: 'Smart Contract Lint' runs-on: 'ubuntu-latest' defaults: run: - working-directory: './cow-trader' + working-directory: './smart-contract-infra' steps: - - name: 'Checkout the repo' + - name: 'Check out the repo' uses: 'actions/checkout@v4' - - name: 'Install UV' - uses: 'astral-sh/setup-uv@v5' - with: - version: '0.5.26' + - name: 'Install Foundry' + uses: 'foundry-rs/foundry-toolchain@v1' - - name: 'Setup Python' - uses: 'actions/setup-python@v5' - with: - python-version-file: './cow-trader/pyproject.toml' + - name: 'Install Bun' + uses: 'oven-sh/setup-bun@v1' - - name: 'Install dependencies' - run: 'uv sync --all-extras --dev' + - name: 'Install the Node.js dependencies' + run: 'bun install' - - name: 'Install Ape' - uses: 'ApeWorX/github-action@v3' - with: - python-version: '3.11' - ape-version-pin: '0.8.25' - ape-plugins-list: 'solidity==0.8.5 alchemy==0.8.7 etherscan==0.8.4 foundry==0.8.7' + - name: 'Lint the code' + run: 'bun run lint' - - name: 'Install contract dependencies' - run: 'ape pm install' + - name: 'Add lint summary' + run: | + echo "## Lint result" >> $GITHUB_STEP_SUMMARY + echo "✅ Passed" >> $GITHUB_STEP_SUMMARY - - name: 'Compile contracts' - run: 'ape compile --force' + contracts-build: + name: 'Smart Contract Build' + runs-on: 'ubuntu-latest' + defaults: + run: + working-directory: './smart-contract-infra' + steps: + - name: 'Check out the repo' + uses: 'actions/checkout@v4' + + - name: 'Install Foundry' + uses: 'foundry-rs/foundry-toolchain@v1' + + - name: 'Install Bun' + uses: 'oven-sh/setup-bun@v1' + + - name: 'Install the Node.js dependencies' + run: 'bun install' + + - name: 'Build the contracts and print their size' + run: 'forge build --sizes' - name: 'Add build summary' run: | echo "## Build result" >> $GITHUB_STEP_SUMMARY echo "✅ Passed" >> $GITHUB_STEP_SUMMARY - trader-test: - name: 'CoW Trader Test' - needs: ['trader-lint', 'trader-build'] + contracts-test: + name: 'Smart Contract Test' + needs: ['contracts-lint', 'contracts-build'] runs-on: 'ubuntu-latest' - env: - UV_SYSTEM_PYTHON: 1 defaults: run: - working-directory: './cow-trader' + working-directory: './smart-contract-infra' steps: - - name: 'Checkout the repo' + - name: 'Check out the repo' uses: 'actions/checkout@v4' - - name: 'Install UV' - uses: 'astral-sh/setup-uv@v5' - with: - version: '0.5.26' + - name: 'Install Foundry' + uses: 'foundry-rs/foundry-toolchain@v1' - - name: 'Setup Python' - uses: 'actions/setup-python@v5' - with: - python-version-file: './cow-trader/pyproject.toml' - - - name: 'Install dependencies' - run: 'uv sync --all-extras --dev' - - - name: 'Install Ape' - uses: 'ApeWorX/github-action@v3' - with: - python-version: '3.11' - ape-version-pin: '0.8.25' - ape-plugins-list: 'solidity==0.8.5 alchemy==0.8.7 etherscan==0.8.4 foundry==0.8.7' + - name: 'Install Bun' + uses: 'oven-sh/setup-bun@v1' - - name: 'Install contract dependencies' - run: 'ape pm install' + - name: 'Install the Node.js dependencies' + run: 'bun install' - - name: 'Run tests' - run: 'ape test -s' + - name: 'Run the tests' + run: 'forge test' - name: 'Add test summary' run: | From 32fa1319cae3366a7ca6b733b8e8a47816b5b779 Mon Sep 17 00:00:00 2001 From: lumoswiz Date: Fri, 7 Feb 2025 18:39:21 +0100 Subject: [PATCH 33/37] test: add initial test suite for TokenAllowlist Add basic test setup with: - Base test contract with common setup - Constants contract for addresses - Utils contract for test helpers - Initial TokenAllowlist unit tests --- smart-contract-infra/test/Base.t.sol | 23 +++++++++++++++++++ .../test/unit/TokenAllowlist.t.sol | 21 +++++++++++++++++ smart-contract-infra/test/utils/Constants.sol | 16 +++++++++++++ smart-contract-infra/test/utils/Utils.sol | 12 ++++++++++ 4 files changed, 72 insertions(+) create mode 100644 smart-contract-infra/test/Base.t.sol create mode 100644 smart-contract-infra/test/unit/TokenAllowlist.t.sol create mode 100644 smart-contract-infra/test/utils/Constants.sol create mode 100644 smart-contract-infra/test/utils/Utils.sol diff --git a/smart-contract-infra/test/Base.t.sol b/smart-contract-infra/test/Base.t.sol new file mode 100644 index 0000000..3f7f2bb --- /dev/null +++ b/smart-contract-infra/test/Base.t.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.28; + +import { TokenAllowlist } from "src/TokenAllowlist.sol"; +import { Constants } from "test/utils/Constants.sol"; +import { Utils } from "test/utils/Utils.sol"; + +contract BaseTest is Constants, Utils { + address internal alice; + address internal dao; + + TokenAllowlist internal allowlist; + + function setUp() public { + alice = makeAddr("alice"); + dao = makeAddr("dao"); + + vm.startPrank(dao); + allowlist = new TokenAllowlist(); + + // Default caller: dao + } +} diff --git a/smart-contract-infra/test/unit/TokenAllowlist.t.sol b/smart-contract-infra/test/unit/TokenAllowlist.t.sol new file mode 100644 index 0000000..329e251 --- /dev/null +++ b/smart-contract-infra/test/unit/TokenAllowlist.t.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.28; + +import { BaseTest } from "test/Base.t.sol"; + +contract Concrete_Unit_TokenAllowlist is BaseTest { + function test_ShouldRevert_AddToken() external { + resetPrank(alice); + vm.expectRevert(); + allowlist.addToken(GNO); + } + + modifier whenOwner() { + _; + } + + function test_AddToken() external whenOwner { + allowlist.addToken(GNO); + assertEq(allowlist.isAllowed(GNO), true); + } +} diff --git a/smart-contract-infra/test/utils/Constants.sol b/smart-contract-infra/test/utils/Constants.sol new file mode 100644 index 0000000..7737faa --- /dev/null +++ b/smart-contract-infra/test/utils/Constants.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.28; + +contract Constants { + /// @notice Gnosis Chain GNO address + address internal constant GNO = 0x9C58BAcC331c9aa871AFD802DB6379a98e80CEdb; + + /// @notice Gnosis Chain COW address + address internal constant COW = 0x177127622c4A00F3d409B75571e12cB3c8973d3c; + + /// @notice Gnosis Chain WETH address + address internal constant WETH = 0x6A023CCd1ff6F2045C3309768eAd9E68F978f6e1; + + /// @notice Gnosis Chain SAFE address + address internal constant SAFE = 0x5aFE3855358E112B5647B952709E6165e1c1eEEe; +} diff --git a/smart-contract-infra/test/utils/Utils.sol b/smart-contract-infra/test/utils/Utils.sol new file mode 100644 index 0000000..1383c49 --- /dev/null +++ b/smart-contract-infra/test/utils/Utils.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.28; + +import { Test } from "forge-std/Test.sol"; + +contract Utils is Test { + /// @notice Stops the active prank and starts a new one with `_msgSender` + function resetPrank(address _msgSender) internal { + vm.stopPrank(); + vm.startPrank(_msgSender); + } +} From 23b051faf7d82a66046a7e0a05c7240ab02e462d Mon Sep 17 00:00:00 2001 From: lumoswiz Date: Fri, 7 Feb 2025 18:41:21 +0100 Subject: [PATCH 34/37] fix: package.json linting --- smart-contract-infra/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smart-contract-infra/package.json b/smart-contract-infra/package.json index 3381606..2db47dc 100644 --- a/smart-contract-infra/package.json +++ b/smart-contract-infra/package.json @@ -25,4 +25,4 @@ "test:coverage": "forge coverage", "test:coverage:report": "forge coverage --report lcov && genhtml lcov.info --branch-coverage --output-dir coverage" } -} \ No newline at end of file +} From 5e2c38de67019b15f77591d1a6ba8dc284d24cd4 Mon Sep 17 00:00:00 2001 From: lumoswiz Date: Fri, 7 Feb 2025 18:53:18 +0100 Subject: [PATCH 35/37] docs: add natspect --- smart-contract-infra/src/CoWSwapGuard.sol | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/smart-contract-infra/src/CoWSwapGuard.sol b/smart-contract-infra/src/CoWSwapGuard.sol index 242776a..f8d5a5e 100644 --- a/smart-contract-infra/src/CoWSwapGuard.sol +++ b/smart-contract-infra/src/CoWSwapGuard.sol @@ -5,13 +5,20 @@ import { Enum } from "@gnosis.pm/safe-contracts/contracts/common/Enum.sol"; import { BaseGuard } from "@gnosis-guild/zodiac/contracts/guard/BaseGuard.sol"; contract CoWSwapGuard is BaseGuard { + /// @notice Thrown when the target address is not GPv2Settlement contract address error InvalidAddress(); + + /// @notice Thrown when the function selector is not `setPreSignature(bytes,bool)` error InvalidSelector(); + /// @notice GPv2Settlement address + /// @dev Deterministically deployed address internal constant GPV2_SETTLEMENT_ADDRESS = 0x9008D19f58AAbD9eD0D60971565AA8510560ab41; + /// @notice Function selector for GPv2Settlement.setPreSignature bytes4 internal constant SET_PRE_SIGNATURE_SELECTOR = 0xec6cb13f; + /// @dev Module transactions only use the first four parameters: to, value, data, and operation. function checkTransaction( address to, uint256, From a8000ca2e7c5adc2ba00526e058c599bd75d8138 Mon Sep 17 00:00:00 2001 From: lumoswiz Date: Fri, 7 Feb 2025 23:55:22 +0100 Subject: [PATCH 36/37] refactor: separate concerns between TradingModule and CoWSwapGuard TradingModule now focuses on order validation and execution CoWSwapGuard enforces CoW Swap specific rules: - Target contract must be GPv2Settlement - Tokens must be CoW DAO allowed - Function must be setPreSignature --- smart-contract-infra/foundry.toml | 1 - smart-contract-infra/src/CoWSwapGuard.sol | 23 ++++++++- smart-contract-infra/src/TradingModule.sol | 56 ++++++++++------------ 3 files changed, 46 insertions(+), 34 deletions(-) diff --git a/smart-contract-infra/foundry.toml b/smart-contract-infra/foundry.toml index e8e61b4..49d458b 100644 --- a/smart-contract-infra/foundry.toml +++ b/smart-contract-infra/foundry.toml @@ -8,7 +8,6 @@ out = "out" script = "script" solc = "0.8.28" src = "src" -test = "tests" [profile.ci] fuzz = { runs = 10_000 } diff --git a/smart-contract-infra/src/CoWSwapGuard.sol b/smart-contract-infra/src/CoWSwapGuard.sol index f8d5a5e..83803fe 100644 --- a/smart-contract-infra/src/CoWSwapGuard.sol +++ b/smart-contract-infra/src/CoWSwapGuard.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.28; +import { ITokenAllowlist } from "src/interfaces/ITokenAllowlist.sol"; import { Enum } from "@gnosis.pm/safe-contracts/contracts/common/Enum.sol"; import { BaseGuard } from "@gnosis-guild/zodiac/contracts/guard/BaseGuard.sol"; @@ -11,6 +12,9 @@ contract CoWSwapGuard is BaseGuard { /// @notice Thrown when the function selector is not `setPreSignature(bytes,bool)` error InvalidSelector(); + /// @notice Thrown when either the buy or sell token for an order is not allowed + error OrderNotAllowed(); + /// @notice GPv2Settlement address /// @dev Deterministically deployed address internal constant GPV2_SETTLEMENT_ADDRESS = 0x9008D19f58AAbD9eD0D60971565AA8510560ab41; @@ -18,6 +22,14 @@ contract CoWSwapGuard is BaseGuard { /// @notice Function selector for GPv2Settlement.setPreSignature bytes4 internal constant SET_PRE_SIGNATURE_SELECTOR = 0xec6cb13f; + /// @notice CoW DAO's governance controlled token allowlist contract + ITokenAllowlist internal immutable ALLOWLIST; + + constructor(address _allowlist) { + ALLOWLIST = ITokenAllowlist(_allowlist); + } + + /// @notice Checks the trade is on CoW Swap for allowed tokens and sets the pre-signed order tradeability /// @dev Module transactions only use the first four parameters: to, value, data, and operation. function checkTransaction( address to, @@ -33,12 +45,19 @@ contract CoWSwapGuard is BaseGuard { address ) external - pure + view override { require(to == GPV2_SETTLEMENT_ADDRESS, InvalidAddress()); - (bytes4 selector,,) = abi.decode(data, (bytes4, bytes, bool)); + (bytes memory txData, address sellToken, address buyToken) = abi.decode(data, (bytes, address, address)); + + require(ALLOWLIST.isOrderAllowed(sellToken, buyToken), OrderNotAllowed()); + + bytes32 selector; + assembly { + selector := mload(add(txData, 32)) + } require(selector == SET_PRE_SIGNATURE_SELECTOR, InvalidSelector()); } diff --git a/smart-contract-infra/src/TradingModule.sol b/smart-contract-infra/src/TradingModule.sol index 8830b0e..f8f6637 100644 --- a/smart-contract-infra/src/TradingModule.sol +++ b/smart-contract-infra/src/TradingModule.sol @@ -9,35 +9,36 @@ import { GPv2Signing } from "cowprotocol/contracts/src/contracts/mixins/GPv2Sign import { IAvatar } from "@gnosis-guild/zodiac/contracts/interfaces/IAvatar.sol"; import { IGuard } from "@gnosis-guild/zodiac/contracts/interfaces/IGuard.sol"; -import { ITokenAllowlist } from "./interfaces/ITokenAllowlist.sol"; abstract contract TradingModule is Module, Guardable { using GPv2Order for bytes; - event SetOrderTradeable( - bytes indexed orderUid, - address indexed sellToken, - address indexed buyToken, - uint256 sellAmount, - uint256 buyAmount - ); + /// @notice Emitted when an order was successfully set + event SetOrder(bytes indexed orderUid, bool indexed signed); + /// @notice Thrown when the supplied order UID and order is mismatched error InvalidOrderUID(); - error OrderNotAllowed(); + /// @notice Thrown when the transaction cannot execute + error CannotExec(); + + /// @notice GPv2Settlement address + /// @dev Deterministically deployed address public constant GPV2_SETTLEMENT_ADDRESS = 0x9008D19f58AAbD9eD0D60971565AA8510560ab41; - ITokenAllowlist internal allowlist; + /// @notice CoW Swap Guard contract address + address public constant COW_SWAP_GUARD = 0x9008D19f58AAbD9eD0D60971565AA8510560ab41; // placeholder + + /// @notice GPv2Settlement domain separator bytes32 internal domainSeparator; - constructor(address _owner, address _avatar, address _target, address _guard, address _tokenAllowlist) { - bytes memory initParams = abi.encode(_owner, _avatar, _target, _guard, _tokenAllowlist); + constructor(address _owner, address _avatar, address _target) { + bytes memory initParams = abi.encode(_owner, _avatar, _target); setUp(initParams); } function setUp(bytes memory initParams) public override initializer { - (address _owner, address _avatar, address _target, address _guard, address _tokenAllowlist) = - abi.decode(initParams, (address, address, address, address, address)); + (address _owner, address _avatar, address _target) = abi.decode(initParams, (address, address, address)); require(_avatar != address(0)); require(_target != address(0)); @@ -46,28 +47,23 @@ abstract contract TradingModule is Module, Guardable { setAvatar(_avatar); setTarget(_target); - guard = _guard; - allowlist = ITokenAllowlist(_tokenAllowlist); domainSeparator = GPv2Signing(GPV2_SETTLEMENT_ADDRESS).domainSeparator(); transferOwnership(_owner); } - function setOrderTradeable(bytes memory orderUid, GPv2Order.Data memory order) external { - bytes memory uid; + function setOrder(bytes memory orderUid, GPv2Order.Data memory order, bool signed) external { + bytes memory uid = new bytes(GPv2Order.UID_LENGTH); uid.packOrderUidParams(GPv2Order.hash(order, domainSeparator), owner(), order.validTo); - require(keccak256(orderUid) == keccak256(uid), InvalidOrderUID()); - require(allowlist.isOrderAllowed(address(order.sellToken), address(order.buyToken)), OrderNotAllowed()); + bytes memory txData = abi.encodeCall(GPv2Signing.setPreSignature, (orderUid, signed)); + bytes memory data = abi.encode(txData, address(order.sellToken), address(order.buyToken)); - bytes memory data = abi.encodeWithSignature("setPreSignature(bytes,bool)", orderUid, true); - require(exec(GPV2_SETTLEMENT_ADDRESS, 0, data, Enum.Operation.Call)); + require(exec(GPV2_SETTLEMENT_ADDRESS, 0, data, Enum.Operation.Call), CannotExec()); - emit SetOrderTradeable( - orderUid, address(order.sellToken), address(order.buyToken), order.sellAmount, order.buyAmount - ); + emit SetOrder(orderUid, signed); } function exec( @@ -80,12 +76,10 @@ abstract contract TradingModule is Module, Guardable { override returns (bool) { - address currentGuard = guard; - if (currentGuard != address(0)) { - IGuard(currentGuard).checkTransaction( - to, value, data, operation, 0, 0, 0, address(0), payable(0), "", msg.sender - ); - } + IGuard(COW_SWAP_GUARD).checkTransaction( + to, value, data, operation, 0, 0, 0, address(0), payable(0), "", msg.sender + ); + return IAvatar(target).execTransactionFromModule(to, value, data, operation); } } From 55d1faa704248b870c60d0cc1324af248f1c85fe Mon Sep 17 00:00:00 2001 From: lumoswiz Date: Sat, 8 Feb 2025 00:08:57 +0100 Subject: [PATCH 37/37] fix: pass transaction data to the target --- smart-contract-infra/src/TradingModule.sol | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/smart-contract-infra/src/TradingModule.sol b/smart-contract-infra/src/TradingModule.sol index f8f6637..18310da 100644 --- a/smart-contract-infra/src/TradingModule.sol +++ b/smart-contract-infra/src/TradingModule.sol @@ -80,6 +80,8 @@ abstract contract TradingModule is Module, Guardable { to, value, data, operation, 0, 0, 0, address(0), payable(0), "", msg.sender ); - return IAvatar(target).execTransactionFromModule(to, value, data, operation); + (bytes memory txData,,) = abi.decode(data, (bytes, address, address)); + + return IAvatar(target).execTransactionFromModule(to, value, txData, operation); } }