@0x/contracs-asset-proxy: Pass in minimum buy amounts in the UniswapBridge.

`@0x/contracs-asset-proxy`: Slight refactors in `UniswapBridge`.
This commit is contained in:
Lawrence Forman 2019-10-05 16:27:24 -05:00
parent 584f8b13fe
commit e67888d65f
3 changed files with 42 additions and 47 deletions

View File

@ -34,12 +34,13 @@ contract UniswapBridge is
IWallet IWallet
{ {
/* Mainnet addresses */ /* Mainnet addresses */
address constant public UNISWAP_EXCHANGE_FACTORY_ADDRESS = 0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95; address constant private UNISWAP_EXCHANGE_FACTORY_ADDRESS = 0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95;
address constant public WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; address constant private WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
// Struct to hold `withdrawTo()` local variables in memory and to avoid // Struct to hold `withdrawTo()` local variables in memory and to avoid
// stack overflows. // stack overflows.
struct WithdrawToState { struct WithdrawToState {
address exchangeTokenAddress;
IUniswapExchange exchange; IUniswapExchange exchange;
uint256 fromTokenBalance; uint256 fromTokenBalance;
IEtherToken weth; IEtherToken weth;
@ -81,17 +82,19 @@ contract UniswapBridge is
return BRIDGE_SUCCESS; return BRIDGE_SUCCESS;
} }
// Get the exchange for the token pair. // Get the exchange token for the token pair.
state.exchange = _getUniswapExchangeForTokenPair( state.exchangeTokenAddress = _getUniswapExchangeTokenAddressForTokenPair(
fromTokenAddress, fromTokenAddress,
toTokenAddress toTokenAddress
); );
// Get the exchange.
state.exchange = _getUniswapExchangeForToken(state.exchangeTokenAddress);
// Grant an allowance to the exchange. // Grant an allowance to the exchange.
_grantExchangeAllowance(state.exchange); _grantExchangeAllowance(state.exchange, state.exchangeTokenAddress);
// Get our balance of `fromTokenAddress` token. // Get our balance of `fromTokenAddress` token.
state.fromTokenBalance = IERC20Token(fromTokenAddress).balanceOf(address(this)); state.fromTokenBalance = IERC20Token(fromTokenAddress).balanceOf(address(this));
// Get the weth contract. // Get the weth contract.
state.weth = _getWethContract(); state.weth = getWethContract();
// Convert from WETH to a token. // Convert from WETH to a token.
if (fromTokenAddress == address(state.weth)) { if (fromTokenAddress == address(state.weth)) {
@ -100,8 +103,8 @@ contract UniswapBridge is
// Buy as much of `toTokenAddress` token with ETH as possible and // Buy as much of `toTokenAddress` token with ETH as possible and
// transfer it to `to`. // transfer it to `to`.
state.exchange.ethToTokenTransferInput.value(state.fromTokenBalance)( state.exchange.ethToTokenTransferInput.value(state.fromTokenBalance)(
// No minimum buy amount. // Minimum buy amount.
0, amount,
// Expires after this block. // Expires after this block.
block.timestamp, block.timestamp,
// Recipient is `to`. // Recipient is `to`.
@ -114,15 +117,15 @@ contract UniswapBridge is
uint256 ethBought = state.exchange.tokenToEthSwapInput( uint256 ethBought = state.exchange.tokenToEthSwapInput(
// Sell all tokens we hold. // Sell all tokens we hold.
state.fromTokenBalance, state.fromTokenBalance,
// No minimum buy amount. // Minimum buy amount.
0, amount,
// Expires after this block. // Expires after this block.
block.timestamp block.timestamp
); );
// Wrap the ETH. // Wrap the ETH.
state.weth.deposit.value(ethBought)(); state.weth.deposit.value(ethBought)();
// Transfer the WETH to `to`. // Transfer the WETH to `to`.
IERC20Token(toTokenAddress).transfer(to, ethBought); IEtherToken(toTokenAddress).transfer(to, ethBought);
// Convert from one token to another. // Convert from one token to another.
} else { } else {
@ -131,8 +134,8 @@ contract UniswapBridge is
state.exchange.tokenToTokenTransferInput( state.exchange.tokenToTokenTransferInput(
// Sell all tokens we hold. // Sell all tokens we hold.
state.fromTokenBalance, state.fromTokenBalance,
// No minimum buy amount. // Minimum buy amount.
0, amount,
// No minimum intermediate ETH buy amount. // No minimum intermediate ETH buy amount.
0, 0,
// Expires after this block. // Expires after this block.
@ -162,8 +165,8 @@ contract UniswapBridge is
/// @dev Overridable way to get the weth contract. /// @dev Overridable way to get the weth contract.
/// @return token The WETH contract. /// @return token The WETH contract.
function _getWethContract() function getWethContract()
internal public
view view
returns (IEtherToken token) returns (IEtherToken token)
{ {
@ -172,8 +175,8 @@ contract UniswapBridge is
/// @dev Overridable way to get the uniswap exchange factory contract. /// @dev Overridable way to get the uniswap exchange factory contract.
/// @return factory The exchange factory contract. /// @return factory The exchange factory contract.
function _getUniswapExchangeFactoryContract() function getUniswapExchangeFactoryContract()
internal public
view view
returns (IUniswapExchangeFactory factory) returns (IUniswapExchangeFactory factory)
{ {
@ -183,28 +186,33 @@ contract UniswapBridge is
/// @dev Grants an unlimited allowance to the exchange for its token /// @dev Grants an unlimited allowance to the exchange for its token
/// on behalf of this contract. /// on behalf of this contract.
/// @param exchange The Uniswap token exchange. /// @param exchange The Uniswap token exchange.
function _grantExchangeAllowance(IUniswapExchange exchange) /// @param tokenAddress The token address for the exchange.
function _grantExchangeAllowance(IUniswapExchange exchange, address tokenAddress)
private private
{ {
address tokenAddress = exchange.toTokenAddress();
IERC20Token(tokenAddress).approve(address(exchange), uint256(-1)); IERC20Token(tokenAddress).approve(address(exchange), uint256(-1));
} }
/// @dev Retrieves the uniswap exchange contract for a given token pair. /// @dev Retrieves the uniswap exchange token address for a given token pair.
/// @return exchange The exchange contract for the token pair. /// In the case of a WETH-token exchange, this will be the non-WETH token.
function _getUniswapExchangeForTokenPair( /// In th ecase of a token-token exchange, this will be the first token.
/// @param fromTokenAddress The address of the token we are converting from.
/// @param toTokenAddress The address of the token we are converting to.
/// @return exchangeTokenAddress The address of the token for the uniswap
/// exchange.
function _getUniswapExchangeTokenAddressForTokenPair(
address fromTokenAddress, address fromTokenAddress,
address toTokenAddress address toTokenAddress
) )
private private
view view
returns (IUniswapExchange exchange) returns (address exchangeTokenAddress)
{ {
// Whichever isn't WETH is the exchange token. // Whichever isn't WETH is the exchange token.
if (fromTokenAddress != address(_getWethContract())) { if (fromTokenAddress != address(getWethContract())) {
return _getUniswapExchangeForToken(fromTokenAddress); return fromTokenAddress;
} }
return _getUniswapExchangeForToken(toTokenAddress); return toTokenAddress;
} }
/// @dev Retrieves the uniswap exchange contract for a given token. /// @dev Retrieves the uniswap exchange contract for a given token.
@ -214,7 +222,7 @@ contract UniswapBridge is
view view
returns (IUniswapExchange exchange) returns (IUniswapExchange exchange)
{ {
exchange = _getUniswapExchangeFactoryContract().getExchange(tokenAddress); exchange = getUniswapExchangeFactoryContract().getExchange(tokenAddress);
require(address(exchange) != address(0), "NO_UNISWAP_EXCHANGE_FOR_TOKEN"); require(address(exchange) != address(0), "NO_UNISWAP_EXCHANGE_FOR_TOKEN");
return exchange; return exchange;
} }

View File

@ -413,8 +413,8 @@ contract TestUniswapBridge is
} }
// @dev Use `wethToken`. // @dev Use `wethToken`.
function _getWethContract() function getWethContract()
internal public
view view
returns (IEtherToken) returns (IEtherToken)
{ {
@ -422,8 +422,8 @@ contract TestUniswapBridge is
} }
// @dev This contract will double as the Uniswap contract. // @dev This contract will double as the Uniswap contract.
function _getUniswapExchangeFactoryContract() function getUniswapExchangeFactoryContract()
internal public
view view
returns (IUniswapExchangeFactory) returns (IUniswapExchangeFactory)
{ {

View File

@ -176,7 +176,7 @@ blockchainTests.resets('UniswapBridge unit tests', env => {
expect(calls.length).to.eq(1); expect(calls.length).to.eq(1);
expect(calls[0].exchange).to.eq(exchangeAddress); expect(calls[0].exchange).to.eq(exchangeAddress);
expect(calls[0].tokensSold).to.bignumber.eq(opts.fromTokenBalance); expect(calls[0].tokensSold).to.bignumber.eq(opts.fromTokenBalance);
expect(calls[0].minTokensBought).to.bignumber.eq(0); expect(calls[0].minTokensBought).to.bignumber.eq(opts.amount);
expect(calls[0].minEthBought).to.bignumber.eq(0); expect(calls[0].minEthBought).to.bignumber.eq(0);
expect(calls[0].deadline).to.bignumber.eq(blockTime); expect(calls[0].deadline).to.bignumber.eq(blockTime);
expect(calls[0].recipient).to.eq(opts.toAddress); expect(calls[0].recipient).to.eq(opts.toAddress);
@ -232,7 +232,7 @@ blockchainTests.resets('UniswapBridge unit tests', env => {
expect(calls.length).to.eq(1); expect(calls.length).to.eq(1);
expect(calls[0].args.exchange).to.eq(exchangeAddress); expect(calls[0].args.exchange).to.eq(exchangeAddress);
expect(calls[0].args.tokensSold).to.bignumber.eq(opts.fromTokenBalance); expect(calls[0].args.tokensSold).to.bignumber.eq(opts.fromTokenBalance);
expect(calls[0].args.minEthBought).to.bignumber.eq(0); expect(calls[0].args.minEthBought).to.bignumber.eq(opts.amount);
expect(calls[0].args.deadline).to.bignumber.eq(blockTime); expect(calls[0].args.deadline).to.bignumber.eq(blockTime);
calls = filterLogs<WethDepositArgs>( calls = filterLogs<WethDepositArgs>(
logs.slice(calls[0].logIndex as number), logs.slice(calls[0].logIndex as number),
@ -251,19 +251,6 @@ blockchainTests.resets('UniswapBridge unit tests', env => {
expect(calls[0].args.amount).to.bignumber.eq(opts.exchangeFillAmount); expect(calls[0].args.amount).to.bignumber.eq(opts.exchangeFillAmount);
}); });
it('calls `IUniswapExchange.tokenToEthSwapInput()`', async () => {
const { opts, logs, blockTime } = await withdrawToAsync({
toTokenAddress: wethTokenAddress,
});
const calls = filterLogsToArguments<TokenToEthSwapInputArgs>(logs, ContractEvents.TokenToEthSwapInput);
const exchangeAddress = await getExchangeForTokenAsync(opts.fromTokenAddress);
expect(calls.length).to.eq(1);
expect(calls[0].exchange).to.eq(exchangeAddress);
expect(calls[0].tokensSold).to.bignumber.eq(opts.fromTokenBalance);
expect(calls[0].minEthBought).to.bignumber.eq(0);
expect(calls[0].deadline).to.bignumber.eq(blockTime);
});
it('sets allowance for "from" token', async () => { it('sets allowance for "from" token', async () => {
const { opts, logs } = await withdrawToAsync({ const { opts, logs } = await withdrawToAsync({
toTokenAddress: wethTokenAddress, toTokenAddress: wethTokenAddress,
@ -332,7 +319,7 @@ blockchainTests.resets('UniswapBridge unit tests', env => {
); );
expect(calls.length).to.eq(1); expect(calls.length).to.eq(1);
expect(calls[0].args.exchange).to.eq(exchangeAddress); expect(calls[0].args.exchange).to.eq(exchangeAddress);
expect(calls[0].args.minTokensBought).to.bignumber.eq(0); expect(calls[0].args.minTokensBought).to.bignumber.eq(opts.amount);
expect(calls[0].args.deadline).to.bignumber.eq(blockTime); expect(calls[0].args.deadline).to.bignumber.eq(blockTime);
expect(calls[0].args.recipient).to.eq(opts.toAddress); expect(calls[0].args.recipient).to.eq(opts.toAddress);
}); });