-
Notifications
You must be signed in to change notification settings - Fork 0
feat: open position wrapper #14
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
base: master
Are you sure you want to change the base?
Conversation
* check solver on internalSettle function * check only callable through EVC * prevent reentrancy
make settlement the gatekeeper why did I not do this before
This reverts commit e56eac3.
best way to ensure the expected flow is followed exactly
* check solver on internalSettle function * check only callable through EVC * prevent reentrancy
they shouldn't have been in the repository
feat: use new wrapper from upstream
feat: working security
we use the settlement contract, so it shouldn't be needed anymore also soljson.latest is still here
Co-authored-by: Federico Giacon <[email protected]>
Co-authored-by: Federico Giacon <[email protected]>
…otocol/euler-integration-contracts into feat/open-position-wrapper
Co-authored-by: Federico Giacon <[email protected]>
Co-authored-by: Federico Giacon <[email protected]>
Co-authored-by: Federico Giacon <[email protected]>
Co-authored-by: Federico Giacon <[email protected]>
Co-authored-by: Federico Giacon <[email protected]>
Co-authored-by: Federico Giacon <[email protected]>
…otocol/euler-integration-contracts into feat/open-position-wrapper
should be correct now
…ed by wrappedSettle
ensures that all params are included in the signed data (this should already be the case, but now we are extra sure)
…d signatures work as expected the `_consumePreApprovedHash` method was not actually reverting--instead returning a boolean, which broke the security guarentee of consume pre approved hash. And there was no test validating the negative case for pre approved hashes
Co-authored-by: Federico Giacon <[email protected]>
Co-authored-by: Federico Giacon <[email protected]>
Co-authored-by: Federico Giacon <[email protected]>
…ly certain of the evc behavior when a signature is invalid
…otocol/euler-integration-contracts into feat/open-position-wrapper
| function getSignedCalldata(OpenPositionParams memory params) external view returns (bytes memory) { | ||
| (IEVC.BatchItem[] memory items,) = _encodeBatchItemsBefore(memoryLocation(params)); | ||
| return abi.encodePacked( | ||
| abi.encodeCall(IEVC.batch, items), _getApprovalHash(OPEN_POSITION_PARAMS_TYPE_HASH, memoryLocation(params)) | ||
| ); | ||
| } | ||
|
|
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.
Since (1) this logic needs to be repeated for each wrapper and (2) the same packing is also done in the base wrapper, I'd suggest moving this to the base wrapper.
Doing something like the following would also make immediately clear the link between this function and permit:
| function getSignedCalldata(OpenPositionParams memory params) external view returns (bytes memory) { | |
| (IEVC.BatchItem[] memory items,) = _encodeBatchItemsBefore(memoryLocation(params)); | |
| return abi.encodePacked( | |
| abi.encodeCall(IEVC.batch, items), _getApprovalHash(OPEN_POSITION_PARAMS_TYPE_HASH, memoryLocation(params)) | |
| ); | |
| } | |
| function getSignedCalldata(OpenPositionParams memory params) external view returns (bytes memory) { | |
| (IEVC.BatchItem[] memory items,) = _encodeBatchItemsBefore(memoryLocation(params)); | |
| return _encodePermitData(items, params) | |
| ); | |
| } |
under the assumption that encodePermitData is also used in _addEvcBatchItems's permit encoding.
Then in a comment of _encodePermitData we can explain why this is done (right now this comes out of nowhere).
(Yeah this doesn't work as-is because of OPEN_POSITION_PARAMS_TYPE_HASH. I'd suggest trying to make it into an immutable so that the base contract can use it, if this makes things nicer.)
| ); | ||
| } | ||
|
|
||
| function getSignedCalldata(OpenPositionParams memory params) external view returns (bytes memory) { |
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.
This external function is quite important to frontends since the easiest implementation is to call it to build the permit call that needs to be executed. My suggestions are:
- Adding devdocs explaining what this is for (populating permit.data)
- Renaming this function to something clearer. Maybe
encodePermitDataorpreparePermitData?
| returns (IEVC.BatchItem[] memory items, bool needsPermission) | ||
| { | ||
| OpenPositionParams memory params = paramsFromMemory(paramsLocation); | ||
| items = new IEVC.BatchItem[](4); |
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.
Let's make the constant relationship explicit:
| items = new IEVC.BatchItem[](4); | |
| items = new IEVC.BatchItem[](MAX_BATCH_OPERATIONS - 1); |
Ideally there would be a test that makes sure the output of this function is indeed an array of length MAX_BATCH_OPERATIONS - 1 and not more.
| deal(SUSDS, user, 10000e18); | ||
| } | ||
|
|
||
| struct SettlementData { |
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.
There's a lot of code I expect will be repeating in tests for other wrappers, like SettlementData, _setupUserSusdsApproval, part of setUp, _setupUserPreApprovedFlow, _verifyPositionOpened, getOpenPositionSettlement, part of _createPermitSignature, and probably more.
I don't see this as a problem for merging this PR, but it would be a blocker for merging the next ones. 😛
Something nice for reviewability would be an intermediate PR: main -> open position -> (new) make tests more modular but add nothing new -> close position -> collateral swap. The assumption here is that the helpers won't change much in this code review, otherwise since there are so many tests to backport that it would make it significantly hard to integrate changes.
| function test_OpenPositionWrapper_Success() external { | ||
| vm.skip(bytes(forkRpcUrl).length == 0); | ||
|
|
||
| address account = address(uint160(user) ^ 1); |
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.
(Not a code change, just a discussion point.)
Are we guaranteed that the account the order uses is "reserved" for our trade? And if so, how? Is it the Euler frontend managing which accounts to use?
| address[] memory targets = new address[](1); | ||
| bytes[] memory datas = new bytes[](1); | ||
| targets[0] = wrapper; | ||
| datas[0] = abi.encodeWithSignature("wrappedSettle(bytes,bytes)", settleData, wrapperData); | ||
| solver.runBatch(targets, datas); |
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.
I don't understand it, why the need to run this batching setup instead of just pranking an EOA solver and directly execute wrappedSettle? Do we ever need to execute more than one operation from the solvers?
| assertEq(debtBefore, 0, "User should start with no debt"); | ||
| assertEq(susdsBalanceBefore, 0, "User should start with no eSUSDS"); |
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.
I believe this should be moved to where debtBefore and susdsBalanceBefore are defined, and then there should be checks on the updated balances.
| function _verifyPositionOpened( | ||
| address account, | ||
| uint256 expectedCollateral, | ||
| uint256 expectedDebt, | ||
| uint256 allowedDelta | ||
| ) internal view { | ||
| assertApproxEqAbs( | ||
| IEVault(ESUSDS).convertToAssets(IERC20(ESUSDS).balanceOf(account)), | ||
| expectedCollateral, | ||
| allowedDelta, | ||
| "User should have collateral deposited" | ||
| ); | ||
| assertEq(IEVault(EWETH).debtOf(account), expectedDebt, "User should have debt"); |
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.
This function hardcodes the vaults to ESUSDS and EWETH but many other don't. The most important thing to me is consistency, but in general I think it makes more sense to make the vault generic since it's one of the tokens and we may want to change them in other tests.
| executeWrappedSettlement(address(openPositionWrapper), settleData, wrapperData); | ||
|
|
||
| // Verify position was created successfully | ||
| _verifyPositionOpened(account, DEFAULT_BUY_AMOUNT + SUSDS_MARGIN, DEFAULT_BORROW_AMOUNT, 1 ether); |
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.
This function is hard to read, mostly because it has too many similar parameters. Here it makes sense to make the parameters explicit.
| _verifyPositionOpened(account, DEFAULT_BUY_AMOUNT + SUSDS_MARGIN, DEFAULT_BORROW_AMOUNT, 1 ether); | |
| _verifyPositionOpened({ | |
| account: account, | |
| expectedCollateral: DEFAULT_BUY_AMOUNT + SUSDS_MARGIN, | |
| expectedDebt: DEFAULT_BORROW_AMOUNT, | |
| allowedDelta: 1 ether | |
| }); |
| executeWrappedSettlement(address(openPositionWrapper), settleData, wrapperData); | ||
|
|
||
| // Verify position was created successfully | ||
| _verifyPositionOpened(account, DEFAULT_BUY_AMOUNT + SUSDS_MARGIN, DEFAULT_BORROW_AMOUNT, 1 ether); |
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.
Why do we need approximate equality in the first place? Can't we make this an exact equality?
Also, it's unclear what the delta is for, is it the collateral or the debt?
Isn't there a way for us to estimate the error exactly? What creates confusion in the amounts?
Description
Implements the
CowEvcOpenPositionWrapperin order to satisfy a usecase for the Euler integration.Context
Read up on notion
Considerations
collateralAmountto 0)Out of Scope
Every line of code in this PR should be considered in-scope.
Testing Instructions
Follow the test command instructions in the README
We want to make sure:
fixes https://linear.app/cowswap/issue/COW-65/initial-merge-contract-of-cowevcopenpositionwrapper