Skip to content
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

Paraswap Integration for TokenExchangerV2 #121

Merged
merged 19 commits into from
Jul 15, 2020
Merged

Paraswap Integration for TokenExchangerV2 #121

merged 19 commits into from
Jul 15, 2020

Conversation

olivdb
Copy link
Contributor

@olivdb olivdb commented Jun 18, 2020

This PR introduces a new version of the TokenExchanger module that relies on Paraswap instead of Kyber. This brings the following benefits:

  • ERC20 to ERC20 trades
  • Reduced gas cost of every trade
  • Improved price and liquidity, specially on larger trades.
  • Routing to multiple liquidity provider (0x, Uniswap, Kyber, Curve, etc)

@olivdb olivdb self-assigned this Jun 18, 2020
@olivdb olivdb force-pushed the paraswap branch 3 times, most recently from 2e9a89f to 0112dad Compare June 23, 2020 17:46
@olivdb olivdb marked this pull request as ready for review July 1, 2020 18:55
@@ -33,139 +35,238 @@ contract TokenExchanger is OnlyOwnerModule {

// Mock token address for ETH
address constant internal ETH_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
// Signatures of Paraswap's trade methods
bytes4 constant internal MULTISWAP = 0xcbd1603e;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe useful to have the computation of the signatures in the comment (bytes4(keccak256(.....)) so we know exactly what is called.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

*/
function trade(
function multiSwap(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not name this method sell since the other one is called buy? It will probably avoid some confusion.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I hesitated but in the end decided to use naming consistent with Paraswap's contract (which uses buy and multiSwap). Open to both.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would go for buy and sell irrespective of their names in Paraswap. In the end it will make it clearer for us what each method does (i.e. simpler for clients, support, etc).

// TODO: Use a "safe approve" logic similar to the one implemented below in other modules
if (_token != ETH_TOKEN_ADDRESS) {
_existingAllowance = ERC20(_token).allowance(_wallet, paraswapProxy);
if (_existingAllowance < uint256(-1)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this condition necessary?

Copy link
Contributor Author

@olivdb olivdb Jul 14, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the user has granted an unlimited allowance to Paraswap (via WalletConnect), we should not change the allowance. That's not only to save gas in this particular transaction, but also because many tokens (DAI, USDT, etc) optimise transferFrom when the allowance is uint(-1) so as to save gas and I think we should not break this optimisation when the user opts for it.

Copy link
Contributor Author

@olivdb olivdb Jul 14, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, without this condition, SafeMath.add(_existingAllowance, _amount) would revert when _existingAllowance == uint(-1).

paraswapProxy = IAugustusSwapper(_paraswap).getTokenTransferProxy();
referrer = _referrer;

for (uint i = 0; i < _authorisedExchanges.length; i++) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think using a black list instead of a white list would give us more flexibility. When Paraswap adds a new DEX we will want to benefit from it without having to redeploy a new module. That's one of the main benefit of using an aggregator. We should only redeploy a new module when the added DEX does not meet our security requirements.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the record we have decided to keep the (fixed) whitelist as it is the only option that does not place any security/trust assumption on both Argent and Paraswap.

uint256 _minConversionRate
uint256 _maxSrcAmount,
uint256 _destAmount,
uint256 _expectedDestAmount,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is _expectedDestAmount returned by the API and is it different than _destAmount in a buy? Maybe _destAmount is not needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a typo: it should be _expectedSrcAmount. It's given by the API and is only used by Paraswap's Bought event.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

uint256 _minConversionRate
uint256 _maxSrcAmount,
uint256 _destAmount,
uint256 _expectedSrcAmount,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a detail but I would expect the _expectedSrcAmount to be right after the _maxSrcAmount in the parameters of the method so you have the expected and the worst case amounts next to each other. I think it makes the inputs more readable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed and fixed

paraswapProxy = IAugustusSwapper(_paraswap).getTokenTransferProxy();
referrer = _referrer;

for (uint i = 0; i < _authorisedExchanges.length; i++) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the record we have decided to keep the (fixed) whitelist as it is the only option that does not place any security/trust assumption on both Argent and Paraswap.

@olivdb olivdb merged commit 316c3f9 into develop Jul 15, 2020
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.

2 participants