Skip to content

Conversation

@karczuRF
Copy link
Collaborator

@karczuRF karczuRF commented Feb 7, 2022

No description provided.

(amountInTokenA > 0 && amountInTokenB == 0) || (amountInTokenA == 0 && amountInTokenB > 0),
'DXswapRelayer: INVALID_TOKEN_AMOUNT'
);
require(priceTolerance <= PARTS_PER_MILLION, 'DXswapRelayer: INVALID_TOLERANCE');
Copy link
Collaborator

@kowalski kowalski Feb 7, 2022

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');
Copy link

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,
Copy link

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);
Copy link

@luzzif luzzif May 11, 2022

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).

Copy link
Collaborator Author

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)) {
Copy link

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;
Copy link

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.

Copy link
Collaborator Author

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);
Copy link

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);
Copy link

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).

Copy link
Collaborator Author

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);
Copy link

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)?

Copy link
Collaborator Author

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?

Copy link

@luzzif luzzif left a 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;
Copy link

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;
Copy link

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);
Copy link

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;
Copy link

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
Copy link

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;
Copy link

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;
Copy link

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;
Copy link

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;
Copy link

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);
Copy link

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.

Federico Luzzi and others added 9 commits September 1, 2022 12:38
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants