-
Notifications
You must be signed in to change notification settings - Fork 4
Add dxswapSwapRelayer #39
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: develop
Are you sure you want to change the base?
Conversation
| (amountInTokenA > 0 && amountInTokenB == 0) || (amountInTokenA == 0 && amountInTokenB > 0), | ||
| 'DXswapRelayer: INVALID_TOKEN_AMOUNT' | ||
| ); | ||
| require(priceTolerance <= PARTS_PER_MILLION, 'DXswapRelayer: INVALID_TOLERANCE'); |
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.
to consult with John: it may require that priceTolerance is at least 0.3% as this the fee uniswap charges.
However, it dxswap has different fee model, than perhaps it's better to leave as is
| } else { | ||
| require(IERC20(tokenA).balanceOf(address(this)) >= amountInTokenA, 'DXswapRelayer: INSUFFICIENT_TOKEN_A'); | ||
| } | ||
| require(IERC20(tokenB).balanceOf(address(this)) >= amountInTokenB, 'DXswapRelayer: INSUFFICIENT_TOKEN_B'); |
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.
If only one of the 2 tokens can have an amount > 0 (as per one of the requires above), we can shave off some gas by only checking the balance in the non-zero token amount (the other will always be 0).
| orders[orderIndex] = Order({ | ||
| tokenIn: amountInTokenA > 0 ? tokenA : tokenB, | ||
| tokenOut: amountInTokenA > 0 ? tokenB : tokenA, | ||
| amountIn: amountInTokenA > 0 ? amountInTokenA : amountInTokenB, |
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.
These checks can all be collapsed into one, saving some gas.
(address tokenIn, address tokenOut, uint256 amountIn) = amountInTokenA > 0 ? (tokenA, tokenB, amountInTokenA) : (tokenB, tokenA, amountInTokenB);
| Order storage order = orders[orderIndex]; | ||
| require(block.timestamp <= order.deadline, 'DXswapRelayer: DEADLINE_REACHED'); | ||
| require(!oracleCreator.isOracleFinalized(order.oracleId), 'DXswapRelayer: OBSERVATION_ENDED'); | ||
| uint256 amountBounty = GAS_ORACLE_UPDATE.mul(tx.gasprice).add(BOUNTY); |
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'd maybe cap the gas price to BASEFEE + a certain maximum amount as a miner tip (let's say 5 or 6 gwei?). I can see grief attacks where someone submits a tx with a high gas price, draining the contract of all ETH. At that point oracles become unworkable because there are no funds to pay bounties (it can also be bad if we want to perform an ETH trade because the ETH can be drained by a malicious user). The user would only gain the bounty, but it would be pretty damn bad for the DAO (although very good for miners).
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.
Nice catch! Agree that would be good to use basefee, but to do that we need to upgrade Solidity, because block.basefee was introduced with 0.8.7. I suggest upgrading to the newest version anyways.
I'm not sure about a miner tip, what do you think @jpkcambridge?
| address[] memory path = new address[](2); | ||
| address swapRouter; | ||
|
|
||
| if (_tokenIn == address(0)) { |
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.
Preeeetty sure we can leave this to the router and do a check instead. If tokenIn is ETH, we can use swapExactTokensForETH, swapExactTokensForTokens instead. Just a tip though, not a hard requirement.
| } | ||
|
|
||
| path[0] = _tokenIn; | ||
| path[1] = _tokenOut; |
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.
Do we intend to always do direct swaps without hops? If not, we could manually specify the path when creating the order. It gives us more flexibility.
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.
As I was informed we want to do only direct swaps and looking for better paths is out of scope of this contract.
| } | ||
|
|
||
| TransferHelper.safeApprove(_tokenIn, swapRouter, _amountIn); | ||
| TransferHelper.safeApprove(_tokenOut, swapRouter, _minAmountOut); |
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 think this approval is necessary? The router only needs to take the tokenIn, so only that one should be allowed.
| address(this), | ||
| block.timestamp | ||
| ); | ||
| TransferHelper.safeApprove(_tokenIn, swapRouter, 0); |
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 shouldn't be necessary. It depends on the token really, but 90% of them automatically subtract the allowance when "used up". Plus, we're allowing pretty damn safe contracts (Uniswap-like routers only).
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.
You're right. That was left just "to be sure" about allowance, but can be removed as you explained.
| (uint256 reserveA, uint256 reserveB, ) = IDXswapPair(order.oraclePair).getReserves(); | ||
| require(reserveA >= order.minReserveA && reserveB >= order.minReserveB, 'DXswapRelayer: RESERVE_TOO_LOW'); | ||
|
|
||
| oracleCreator.update(order.oracleId); |
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'm wondering what happens here if the reserves have changed and in turn oracle parameters are potentially not safe anymore? I.e. someone removes liquidity without it being brought below minimum levels, but changing the oracle's window time as calculated in _consultOracleParameters. I would maybe think about consulting the oracle parameters once again and eventually update them for the next read (and potentially reset the update counter)?
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 think it would be ok indeed to consult the oracle and check whether window time has changed or not. Let's keep in minds that the same solution is used for DXswapRelayer contract, so if we consider the oracle might be not safe we will have to change also that one, @jpkcambridge?
luzzif
left a comment
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.
Added a couple comments, but didn't finish the review. Will resume later.
| @@ -0,0 +1,296 @@ | |||
| pragma solidity =0.6.6; | |||
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.
Upgrade Solidity to v0.8.16 and remove the ABI encorder v2 pragma.
| import './../libraries/DXswapLibrary.sol'; | ||
|
|
||
| contract DXswapSwapRelayer { | ||
| using SafeMath for uint256; |
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.
When the Solidity version is upgraded, we can remove SafeMath usages
| contract DXswapSwapRelayer { | ||
| using SafeMath for uint256; | ||
|
|
||
| event NewOrder(uint256 indexed _orderIndex); |
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'd remove the underscore here, in all event param names
| bool executed; | ||
| } | ||
|
|
||
| uint256 public immutable GAS_ORACLE_UPDATE = 168364; |
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'm not sure about this. The underlying protocol can change and call gas costs might be different in the future... using gasleft() to calculate the specific amount of gas used to perform the oracle update is a better way of doing things imho.
|
|
||
| uint256 public immutable GAS_ORACLE_UPDATE = 168364; | ||
| uint256 public immutable PARTS_PER_MILLION = 1000000; | ||
| uint256 public immutable BOUNTY = 0.01 ether; // To be decided |
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.
Calculating the bounty dinamically would probably be best. Let's say as a percentage of the fee used to update the oracle.
| uint256 public immutable PARTS_PER_MILLION = 1000000; | ||
| uint256 public immutable BOUNTY = 0.01 ether; // To be decided | ||
| uint8 public immutable PROVISION = 1; | ||
| uint8 public immutable REMOVAL = 2; |
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.
Pretty much all of the above state variables are constants, so it's heavily suggested to use constant instead of immutable. immutable variables are evaluated at runtime and are reserved 32 bytes even for potentially smaller values. This increases deployment costs for no particular reason.
| address public immutable uniswapRouter; | ||
| address public immutable WETH; | ||
|
|
||
| OracleCreator oracleCreator; |
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.
Visibility specifier missing. Should this be internal?
|
|
||
| OracleCreator oracleCreator; | ||
| uint256 public orderCount; | ||
| mapping(uint256 => Order) orders; |
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.
Same as above.
| address _WETH, | ||
| OracleCreator _oracleCreator | ||
| ) public { | ||
| owner = _owner; |
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.
Are we ok with no validation?
| } | ||
| require(IERC20(tokenB).balanceOf(address(this)) >= amountInTokenB, 'DXswapRelayer: INSUFFICIENT_TOKEN_B'); | ||
|
|
||
| address pair = _pair(tokenA, tokenB, factory); |
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.
We could actually call the factory to get the pair address based on the 2 tokens that are into it. This will also let us know if the pair exists or not. If it doesn't we should abort here.
chore: fast-forward `develop` to `master`
chore: v0.3.23 and fast-forward
* migrate to hardhat * fix solc errors * refactor: change init code pair hash * test: update gas usage checks * cleanup & upgrade packages * refactor after cr: add dependency-compiler & control gas report * refactor: remove flatten script & add deploy
No description provided.