diff --git a/contracts/asset-proxy/contracts/src/bridges/BalancerBridge.sol b/contracts/asset-proxy/contracts/src/bridges/BalancerBridge.sol index c63a8e7935..ef75c586fc 100644 --- a/contracts/asset-proxy/contracts/src/bridges/BalancerBridge.sol +++ b/contracts/asset-proxy/contracts/src/bridges/BalancerBridge.sol @@ -26,14 +26,12 @@ import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol"; import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol"; import "../interfaces/IERC20Bridge.sol"; import "../interfaces/IBalancerPool.sol"; -import "./MixinGasToken.sol"; contract BalancerBridge is IERC20Bridge, IWallet, - DeploymentConstants, - MixinGasToken + DeploymentConstants { /// @dev Callback for `IERC20Bridge`. Tries to buy `amount` of /// `toTokenAddress` tokens by selling the entirety of the `fromTokenAddress` @@ -53,7 +51,6 @@ contract BalancerBridge is bytes calldata bridgeData ) external - freesGasTokensFromCollector returns (bytes4 success) { // Decode the bridge data. diff --git a/contracts/integrations/test/bridges/balancer_bridge_mainnet_test.ts b/contracts/integrations/test/bridges/balancer_bridge_mainnet_test.ts index cdd43b1c93..270062624f 100644 --- a/contracts/integrations/test/bridges/balancer_bridge_mainnet_test.ts +++ b/contracts/integrations/test/bridges/balancer_bridge_mainnet_test.ts @@ -1,7 +1,7 @@ import { artifacts as assetProxyArtifacts } from '@0x/contracts-asset-proxy'; import { BalancerBridgeContract } from '@0x/contracts-asset-proxy/lib/src/wrappers'; import { ERC20TokenContract } from '@0x/contracts-erc20'; -import { blockchainTests, constants, toBaseUnitAmount } from '@0x/contracts-test-utils'; +import { blockchainTests, constants, expect, toBaseUnitAmount } from '@0x/contracts-test-utils'; import { AbiEncoder } from '@0x/utils'; const CHONKY_DAI_WALLET = '0x1e0447b19bb6ecfdae1e4ae1694b0c3659614e4e'; // dydx solo margin @@ -15,8 +15,9 @@ blockchainTests.configure({ blockchainTests.fork('Mainnet Balancer bridge tests', env => { let testContract: BalancerBridgeContract; + let weth: ERC20TokenContract; + let usdc: ERC20TokenContract; const receiver = '0x986ccf5234d9cfbb25246f1a5bfa51f4ccfcb308'; - const daiAddress = '0x6B175474E89094C44Da98b954EedeAC495271d0F'; const usdcAddress = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'; const wethAddress = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; const wethUsdcBalancerAddress = '0x2471de1547296aadb02cc1af84afe369b6f67c87'; @@ -33,58 +34,70 @@ blockchainTests.fork('Mainnet Balancer bridge tests', env => { { ...env.txDefaults, from: CHONKY_DAI_WALLET, gasPrice: 0 }, {}, ); + weth = new ERC20TokenContract(wethAddress, env.provider, env.txDefaults); + usdc = new ERC20TokenContract(usdcAddress, env.provider, env.txDefaults); }); blockchainTests.resets('Can trade with two-asset pool', () => { it('successfully exchanges WETH for USDC', async () => { const bridgeData = bridgeDataEncoder.encode([wethAddress, wethUsdcBalancerAddress]); // Fund the Bridge - const weth = new ERC20TokenContract(wethAddress, env.provider, env.txDefaults); await weth .transfer(testContract.address, toBaseUnitAmount(1)) .awaitTransactionSuccessAsync({ from: CHONKY_WETH_WALLET, gasPrice: 0 }, { shouldValidate: false }); + const usdcBalanceBefore = await usdc.balanceOf(receiver).callAsync(); // Exchange via Balancer await testContract .bridgeTransferFrom(usdcAddress, constants.NULL_ADDRESS, receiver, constants.ZERO_AMOUNT, bridgeData) .awaitTransactionSuccessAsync({ from: CHONKY_WETH_WALLET, gasPrice: 0 }, { shouldValidate: false }); + // Check that USDC balance increased + const usdcBalanceAfter = await usdc.balanceOf(receiver).callAsync(); + expect(usdcBalanceAfter).to.be.bignumber.greaterThan(usdcBalanceBefore); }); it('successfully exchanges USDC for WETH', async () => { const bridgeData = bridgeDataEncoder.encode([usdcAddress, wethUsdcBalancerAddress]); // Fund the Bridge - const usdc = new ERC20TokenContract(usdcAddress, env.provider, env.txDefaults); await usdc .transfer(testContract.address, toBaseUnitAmount(1, 6)) .awaitTransactionSuccessAsync({ from: CHONKY_USDC_WALLET, gasPrice: 0 }, { shouldValidate: false }); + const wethBalanceBefore = await weth.balanceOf(receiver).callAsync(); // Exchange via Balancer await testContract .bridgeTransferFrom(wethAddress, constants.NULL_ADDRESS, receiver, constants.ZERO_AMOUNT, bridgeData) .awaitTransactionSuccessAsync({ from: CHONKY_USDC_WALLET, gasPrice: 0 }, { shouldValidate: false }); + const wethBalanceAfter = await weth.balanceOf(receiver).callAsync(); + expect(wethBalanceAfter).to.be.bignumber.greaterThan(wethBalanceBefore); }); }); blockchainTests.resets('Can trade with three-asset pool', () => { it('successfully exchanges WETH for USDC', async () => { const bridgeData = bridgeDataEncoder.encode([wethAddress, wethUsdcDaiBalancerAddress]); // Fund the Bridge - const weth = new ERC20TokenContract(wethAddress, env.provider, env.txDefaults); await weth .transfer(testContract.address, toBaseUnitAmount(1)) .awaitTransactionSuccessAsync({ from: CHONKY_WETH_WALLET, gasPrice: 0 }, { shouldValidate: false }); + const usdcBalanceBefore = await usdc.balanceOf(receiver).callAsync(); // Exchange via Balancer await testContract .bridgeTransferFrom(usdcAddress, constants.NULL_ADDRESS, receiver, constants.ZERO_AMOUNT, bridgeData) .awaitTransactionSuccessAsync({ from: CHONKY_WETH_WALLET, gasPrice: 0 }, { shouldValidate: false }); + // Check that USDC balance increased + const usdcBalanceAfter = await usdc.balanceOf(receiver).callAsync(); + expect(usdcBalanceAfter).to.be.bignumber.greaterThan(usdcBalanceBefore); }); it('successfully exchanges USDC for WETH', async () => { const bridgeData = bridgeDataEncoder.encode([usdcAddress, wethUsdcDaiBalancerAddress]); // Fund the Bridge - const usdc = new ERC20TokenContract(usdcAddress, env.provider, env.txDefaults); await usdc .transfer(testContract.address, toBaseUnitAmount(1, 6)) .awaitTransactionSuccessAsync({ from: CHONKY_USDC_WALLET, gasPrice: 0 }, { shouldValidate: false }); + const wethBalanceBefore = await weth.balanceOf(receiver).callAsync(); // Exchange via Balancer await testContract .bridgeTransferFrom(wethAddress, constants.NULL_ADDRESS, receiver, constants.ZERO_AMOUNT, bridgeData) .awaitTransactionSuccessAsync({ from: CHONKY_USDC_WALLET, gasPrice: 0 }, { shouldValidate: false }); + const wethBalanceAfter = await weth.balanceOf(receiver).callAsync(); + expect(wethBalanceAfter).to.be.bignumber.greaterThan(wethBalanceBefore); }); }); }); diff --git a/packages/asset-swapper/src/utils/market_operation_utils/balancer_utils.ts b/packages/asset-swapper/src/utils/market_operation_utils/balancer_utils.ts index d24da91194..1c7f4514c7 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/balancer_utils.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/balancer_utils.ts @@ -14,11 +14,15 @@ export interface BalancerPool { limitAmount?: BigNumber; } -export const getBalancerPoolsForPairAsync = async (takerToken: string, makerToken: string): Promise => { - return parsePoolData(await getPoolsWithTokens(takerToken, makerToken), takerToken, makerToken); -}; +export async function getBalancerPoolsForPairAsync(takerToken: string, makerToken: string): Promise { + try { + return parsePoolData(await getPoolsWithTokens(takerToken, makerToken), takerToken, makerToken); + } catch (err) { + return []; + } +} -export const computeBalancerSellQuote = (pool: BalancerPool, takerFillAmount: BigNumber): BigNumber => { +export function computeBalancerSellQuote(pool: BalancerPool, takerFillAmount: BigNumber): BigNumber { return bmath.calcOutGivenIn( pool.balanceIn, pool.weightIn, @@ -27,9 +31,9 @@ export const computeBalancerSellQuote = (pool: BalancerPool, takerFillAmount: Bi takerFillAmount, pool.swapFee, ); -}; +} -export const computeBalancerBuyQuote = (pool: BalancerPool, makerFillAmount: BigNumber): BigNumber => { +export function computeBalancerBuyQuote(pool: BalancerPool, makerFillAmount: BigNumber): BigNumber { return bmath.calcInGivenOut( pool.balanceIn, pool.weightIn, @@ -38,4 +42,4 @@ export const computeBalancerBuyQuote = (pool: BalancerPool, makerFillAmount: Big makerFillAmount, pool.swapFee, ); -}; +} diff --git a/packages/asset-swapper/src/utils/market_operation_utils/constants.ts b/packages/asset-swapper/src/utils/market_operation_utils/constants.ts index 5d523df575..4fec2a348c 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/constants.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/constants.ts @@ -55,7 +55,6 @@ export const FEE_QUOTE_SOURCES = [ ERC20BridgeSource.UniswapV2, ERC20BridgeSource.Eth2Dai, ERC20BridgeSource.Kyber, - ERC20BridgeSource.Curve, ]; /** diff --git a/packages/asset-swapper/src/utils/market_operation_utils/curve_utils.ts b/packages/asset-swapper/src/utils/market_operation_utils/curve_utils.ts index 267ef147ce..0ddc3d51a3 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/curve_utils.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/curve_utils.ts @@ -1,9 +1,7 @@ -import * as _ from 'lodash'; - import { MAINNET_CURVE_CONTRACTS } from './constants'; -export const getCurveAddressesForPair = (takerToken: string, makerToken: string): string[] => { - return Object.keys( - _.pickBy(MAINNET_CURVE_CONTRACTS, tokens => tokens.includes(takerToken) && tokens.includes(makerToken)), +export function getCurveAddressesForPair(takerToken: string, makerToken: string): string[] { + return Object.keys(MAINNET_CURVE_CONTRACTS).filter(a => + [makerToken, takerToken].every(t => MAINNET_CURVE_CONTRACTS[a].includes(t)), ); -}; +} diff --git a/packages/asset-swapper/src/utils/market_operation_utils/orders.ts b/packages/asset-swapper/src/utils/market_operation_utils/orders.ts index 7f863403cf..dbde6e8d1e 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/orders.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/orders.ts @@ -222,7 +222,7 @@ function createBridgeOrder(fill: CollapsedFill, opts: CreateOrderFromPathOpts): curveFillData.poolAddress, curveFillData.fromTokenIdx, curveFillData.toTokenIdx, - 1, + 1, // "version" ), ); break; diff --git a/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts b/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts index b49f27add3..ca688245c1 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts @@ -301,14 +301,14 @@ export const samplerOperations = { return { source: ERC20BridgeSource.Balancer, fillData: { poolAddress: pool.id }, - ...samplerOperations.constant(takerFillAmounts.map(_.curry(computeBalancerSellQuote)(pool))), + ...samplerOperations.constant(takerFillAmounts.map(amount => computeBalancerSellQuote(pool, amount))), }; }, getBalancerBuyQuotes(pool: BalancerPool, makerFillAmounts: BigNumber[]): SourceQuoteOperation { return { source: ERC20BridgeSource.Balancer, fillData: { poolAddress: pool.id }, - ...samplerOperations.constant(makerFillAmounts.map(_.curry(computeBalancerBuyQuote)(pool))), + ...samplerOperations.constant(makerFillAmounts.map(amount => computeBalancerBuyQuote(pool, amount))), }; }, getMedianSellRateAsync: async ( @@ -551,7 +551,7 @@ export const samplerOperations = { samplerOperations.getBalancerBuyQuotes(pool, makerFillAmounts), ); default: - throw new Error(`Unsupported sell sample source: ${source}`); + throw new Error(`Unsupported buy sample source: ${source}`); } }, ),