-
Notifications
You must be signed in to change notification settings - Fork 48
feat: implement generic wrapper #247
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
8f1648d
1e8127f
9bfad39
14cc843
9f119d6
277be44
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,74 @@ | ||||||
| // SPDX-License-Identifier: GPL-3.0-or-later | ||||||
| // This program is free software: you can redistribute it and/or modify | ||||||
| // it under the terms of the GNU General Public License as published by | ||||||
| // the Free Software Foundation, either version 3 of the License, or | ||||||
| // (at your option) any later version. | ||||||
|
|
||||||
| // This program is distributed in the hope that it will be useful, | ||||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||||
| // GNU General Public License for more details. | ||||||
|
|
||||||
| // You should have received a copy of the GNU General Public License | ||||||
| // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||||
|
|
||||||
| pragma solidity >=0.7.6 <0.9.0; | ||||||
| pragma abicoder v2; | ||||||
|
|
||||||
| import "./interfaces/IGPv2Wrapper.sol"; | ||||||
|
|
||||||
| /** | ||||||
| * @title A minimalist base that can be extended to safely implement a wrapper. It ensures the most important basic functions of a wrapper are fulfilled | ||||||
| */ | ||||||
| abstract contract GPv2Wrapper is IGPv2Wrapper { | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The name
Suggested change
(This comment applies to every instance of GPv2 in this code.)
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think its better now that implements the interface. I feel a bit unsure lal this code should live in the contracts project. Maybe deserves its own project (technically, its an extension to the protocol so no need to go in the core contracts) |
||||||
| IGPv2Settlement public immutable override UPSTREAM_SETTLEMENT; | ||||||
| GPv2Authentication public immutable override AUTHENTICATOR; | ||||||
|
|
||||||
| constructor(address payable upstreamSettlement_) { | ||||||
| UPSTREAM_SETTLEMENT = IGPv2Settlement(upstreamSettlement_); | ||||||
|
|
||||||
| // retrieve the authentication we are supposed to use from the settlement contract | ||||||
| AUTHENTICATOR = IGPv2Settlement(upstreamSettlement_).authenticator(); | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * @inheritdoc IGPv2Wrapper | ||||||
| */ | ||||||
| function wrappedSettle( | ||||||
| IERC20[] calldata tokens, | ||||||
| uint256[] calldata clearingPrices, | ||||||
| GPv2Trade.Data[] calldata trades, | ||||||
| GPv2Interaction.Data[][3] calldata interactions, | ||||||
| bytes calldata wrapperData | ||||||
| ) external override { | ||||||
| // Revert if not a valid solver | ||||||
| if (!AUTHENTICATOR.isSolver(msg.sender)) { | ||||||
kaze-cow marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
| revert("GPv2Wrapper: not a solver"); | ||||||
| } | ||||||
|
|
||||||
| _wrap(tokens, clearingPrices, trades, interactions, wrapperData); | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * @dev The logic for the wrapper. During this function, `_internalSettle` should be called | ||||||
| */ | ||||||
| function _wrap( | ||||||
| IERC20[] calldata tokens, | ||||||
| uint256[] calldata clearingPrices, | ||||||
| GPv2Trade.Data[] calldata trades, | ||||||
| GPv2Interaction.Data[][3] calldata interactions, | ||||||
| bytes calldata wrapperData | ||||||
| ) internal virtual; | ||||||
|
|
||||||
| /** | ||||||
| * @dev Should be called from within (or otherwise as a result of) _wrap. Calls GPv2Settlement.settle(). | ||||||
| */ | ||||||
| function _internalSettle( | ||||||
| IERC20[] calldata tokens, | ||||||
| uint256[] calldata clearingPrices, | ||||||
| GPv2Trade.Data[] calldata trades, | ||||||
| GPv2Interaction.Data[][3] calldata interactions | ||||||
| ) internal { | ||||||
| UPSTREAM_SETTLEMENT.settle(tokens, clearingPrices, trades, interactions); | ||||||
|
Check failure on line 72 in src/contracts/GPv2Wrapper.sol
|
||||||
| } | ||||||
| } | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,119 @@ | ||
| // SPDX-License-Identifier: LGPL-3.0-or-later | ||
| pragma solidity >=0.7.6 <0.9.0; | ||
| pragma abicoder v2; | ||
|
|
||
| import "./IERC20.sol"; | ||
| import "./GPv2Authentication.sol"; | ||
| import "./IVault.sol"; | ||
| import "../libraries/GPv2Trade.sol"; | ||
| import "../libraries/GPv2Interaction.sol"; | ||
|
|
||
| /// @title Gnosis Protocol v2 Settlement Interface | ||
| /// @author Gnosis Developers | ||
| interface IGPv2Settlement { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If this code isn't move to another repo, I'd suggest using the concrete instance |
||
| /// @dev Event emitted for each executed trade. | ||
| event Trade( | ||
| address indexed owner, | ||
| IERC20 sellToken, | ||
| IERC20 buyToken, | ||
| uint256 sellAmount, | ||
| uint256 buyAmount, | ||
| uint256 feeAmount, | ||
| bytes orderUid | ||
| ); | ||
|
|
||
| /// @dev Event emitted for each executed interaction. | ||
| /// | ||
| /// For gas efficiency, only the interaction calldata selector (first 4 | ||
| /// bytes) is included in the event. For interactions without calldata or | ||
| /// whose calldata is shorter than 4 bytes, the selector will be `0`. | ||
| event Interaction(address indexed target, uint256 value, bytes4 selector); | ||
|
|
||
| /// @dev Event emitted when a settlement completes | ||
| event Settlement(address indexed solver); | ||
|
|
||
| /// @dev Event emitted when an order is invalidated. | ||
| event OrderInvalidated(address indexed owner, bytes orderUid); | ||
|
|
||
| /// @dev The authenticator is used to determine who can call the settle function. | ||
| /// That is, only authorized solvers have the ability to invoke settlements. | ||
| /// Any valid authenticator implements an isSolver method called by the onlySolver | ||
| /// modifier below. | ||
| function authenticator() external view returns (GPv2Authentication); | ||
|
|
||
| /// @dev The Balancer Vault the protocol used for managing user funds. | ||
| function vault() external view returns (IVault); | ||
|
|
||
| /// @dev Map each user order by UID to the amount that has been filled so | ||
| /// far. If this amount is larger than or equal to the amount traded in the | ||
| /// order (amount sold for sell orders, amount bought for buy orders) then | ||
| /// the order cannot be traded anymore. If the order is fill or kill, then | ||
| /// this value is only used to determine whether the order has already been | ||
| /// executed. | ||
| function filledAmount(bytes calldata orderUid) external view returns (uint256); | ||
|
|
||
| /// @dev Settle the specified orders at a clearing price. Note that it is | ||
| /// the responsibility of the caller to ensure that all GPv2 invariants are | ||
| /// upheld for the input settlement, otherwise this call will revert. | ||
| /// Namely: | ||
| /// - All orders are valid and signed | ||
| /// - Accounts have sufficient balance and approval. | ||
| /// - Settlement contract has sufficient balance to execute trades. Note | ||
| /// this implies that the accumulated fees held in the contract can also | ||
| /// be used for settlement. This is OK since: | ||
| /// - Solvers need to be authorized | ||
| /// - Misbehaving solvers will be slashed for abusing accumulated fees for | ||
| /// settlement | ||
| /// - Critically, user orders are entirely protected | ||
| /// | ||
| /// @param tokens An array of ERC20 tokens to be traded in the settlement. | ||
| /// Trades encode tokens as indices into this array. | ||
| /// @param clearingPrices An array of clearing prices where the `i`-th price | ||
| /// is for the `i`-th token in the [`tokens`] array. | ||
| /// @param trades Trades for signed orders. | ||
| /// @param interactions Smart contract interactions split into three | ||
| /// separate lists to be run before the settlement, during the settlement | ||
| /// and after the settlement respectively. | ||
| function settle( | ||
| IERC20[] calldata tokens, | ||
| uint256[] calldata clearingPrices, | ||
| GPv2Trade.Data[] calldata trades, | ||
| GPv2Interaction.Data[][3] calldata interactions | ||
| ) external; | ||
|
|
||
| /// @dev Settle an order directly against Balancer V2 pools. | ||
| /// | ||
| /// @param swaps The Balancer V2 swap steps to use for trading. | ||
| /// @param tokens An array of ERC20 tokens to be traded in the settlement. | ||
| /// Swaps and the trade encode tokens as indices into this array. | ||
| /// @param trade The trade to match directly against Balancer liquidity. The | ||
| /// order will always be fully executed, so the trade's `executedAmount` | ||
| /// field is used to represent a swap limit amount. | ||
| function swap( | ||
| IVault.BatchSwapStep[] calldata swaps, | ||
| IERC20[] calldata tokens, | ||
| GPv2Trade.Data calldata trade | ||
| ) external; | ||
|
|
||
| /// @dev Invalidate onchain an order that has been signed offline. | ||
| /// | ||
| /// @param orderUid The unique identifier of the order that is to be made | ||
| /// invalid after calling this function. The user that created the order | ||
| /// must be the sender of this message. See [`extractOrderUidParams`] | ||
| /// for details on orderUid. | ||
| function invalidateOrder(bytes calldata orderUid) external; | ||
|
|
||
| /// @dev Free storage from the filled amounts of **expired** orders to claim | ||
| /// a gas refund. This method can only be called as an interaction. | ||
| /// | ||
| /// @param orderUids The unique identifiers of the expired order to free | ||
| /// storage for. | ||
| function freeFilledAmountStorage(bytes[] calldata orderUids) external; | ||
|
|
||
| /// @dev Free storage from the pre signatures of **expired** orders to claim | ||
| /// a gas refund. This method can only be called as an interaction. | ||
| /// | ||
| /// @param orderUids The unique identifiers of the expired order to free | ||
| /// storage for. | ||
| function freePreSignatureStorage(bytes[] calldata orderUids) external; | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,48 @@ | ||||||||||||||||||||||||||
| // SPDX-License-Identifier: GPL-3.0-or-later | ||||||||||||||||||||||||||
| // This program is free software: you can redistribute it and/or modify | ||||||||||||||||||||||||||
| // it under the terms of the GNU General Public License as published by | ||||||||||||||||||||||||||
| // the Free Software Foundation, either version 3 of the License, or | ||||||||||||||||||||||||||
| // (at your option) any later version. | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // This program is distributed in the hope that it will be useful, | ||||||||||||||||||||||||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||||||||||||||||||||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||||||||||||||||||||||||
| // GNU General Public License for more details. | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // You should have received a copy of the GNU General Public License | ||||||||||||||||||||||||||
| // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||||||||||||||||||||||||
|
Comment on lines
+2
to
+13
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: as before, not necessary.
Suggested change
|
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| pragma solidity >=0.7.6 <0.9.0; | ||||||||||||||||||||||||||
| pragma abicoder v2; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| import { IERC20 } from "./IERC20.sol"; | ||||||||||||||||||||||||||
| import { IGPv2Settlement } from "./IGPv2Settlement.sol"; | ||||||||||||||||||||||||||
| import { GPv2Authentication } from "./GPv2Authentication.sol"; | ||||||||||||||||||||||||||
| import { GPv2Trade } from "../libraries/GPv2Trade.sol"; | ||||||||||||||||||||||||||
| import { GPv2Interaction } from "../libraries/GPv2Interaction.sol"; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||
| * @dev Interface for wrappers of the GPv2Settlement contract for CoW orders. It serves as a way for the functionality | ||||||||||||||||||||||||||
| * of the settlement contract to be expanded without needing to affect the underlying security. | ||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, it does affect the underlying security of the system. 😛
Suggested change
|
||||||||||||||||||||||||||
| * A wrapper should: | ||||||||||||||||||||||||||
| * * call the equivalent `settle` on the GPv2Settlement contract (0x9008D19f58AAbD9eD0D60971565AA8510560ab41) | ||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The settlement contract address should not be hardcoded in comments (especially if this code is merged into this repo!).
Suggested change
|
||||||||||||||||||||||||||
| * * verify that the caller is authorized via the GPv2Authentication contract registered on the settlement contract. | ||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit, caller authentication may differ in specific implementations.
Suggested change
|
||||||||||||||||||||||||||
| * A wrapper may also execute, or otherwise put the blockchain in a state that needs to be established prior or after settlement. | ||||||||||||||||||||||||||
| * Prior to usage, the wrapper itself needs to be approved by the GPv2Authentication contract. | ||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||
| interface IGPv2Wrapper { | ||||||||||||||||||||||||||
| function UPSTREAM_SETTLEMENT() external view returns (IGPv2Settlement); | ||||||||||||||||||||||||||
|
Check warning on line 34 in src/contracts/interfaces/IGPv2Wrapper.sol
|
||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we want this in the interface? I would think there's implementation that not necessarilly have this
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The wrapper contract doesn't have a way to specify which settlement contract to call, so to me it makes sense to have a general way to expose this information somehow. (Not important though, it's not really necessary.) |
||||||||||||||||||||||||||
| function AUTHENTICATOR() external view returns (GPv2Authentication); | ||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The authentication mechanism doesn't have to be the settlement. This means that this call may be meaningless. Also, the authenticator is always available as
Suggested change
|
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||
| * @dev Called to initiate a wrapped call against the settlement function. Most of the arguments are shared with GPv2Settlement.settle(). | ||||||||||||||||||||||||||
| * @param wrapperData any additional data which is needed for the wrapper to complete its task. | ||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||
| function wrappedSettle( | ||||||||||||||||||||||||||
| IERC20[] calldata tokens, | ||||||||||||||||||||||||||
| uint256[] calldata clearingPrices, | ||||||||||||||||||||||||||
| GPv2Trade.Data[] calldata trades, | ||||||||||||||||||||||||||
| GPv2Interaction.Data[][3] calldata interactions, | ||||||||||||||||||||||||||
|
Comment on lines
+42
to
+45
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would you be able to test what's the gas savings when using a pre-encoded settlement: I'd like to know how much of an overhead it is to encode the call parameters in the wrapper rather than providing the call data ready to be |
||||||||||||||||||||||||||
| bytes calldata wrapperData | ||||||||||||||||||||||||||
| ) external; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,20 @@ | ||||||
| // SPDX-License-Identifier: LGPL-3.0-or-later | ||||||
| // solhint-disable-next-line compiler-version | ||||||
| pragma solidity >=0.7.6 <0.9.0; | ||||||
| pragma abicoder v2; | ||||||
|
Comment on lines
+2
to
+4
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can use Solidity v0.8 for the tests to get rid of the solhint disable, we already do that foll all Foundry tests. |
||||||
|
|
||||||
| import "src/contracts/GPv2Wrapper.sol"; | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: explicit imports are nicer for debugging since it lets you understand more easily where some component comes from at a glance.
Suggested change
|
||||||
|
|
||||||
| contract EmptyWrapper is GPv2Wrapper { | ||||||
| constructor(address payable upstreamSettlement_) GPv2Wrapper(upstreamSettlement_) {} | ||||||
|
|
||||||
| function _wrap( | ||||||
| IERC20[] calldata tokens, | ||||||
| uint256[] calldata clearingPrices, | ||||||
| GPv2Trade.Data[] calldata trades, | ||||||
| GPv2Interaction.Data[][3] calldata interactions, | ||||||
| bytes calldata /*wrappedData*/ | ||||||
| ) internal override { | ||||||
| _internalSettle(tokens, clearingPrices, trades, interactions); | ||||||
| } | ||||||
| } | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: the license preamble is unneeded, contracts in this repo usually don't specify it.