diff --git a/contracts/asset-proxy/CHANGELOG.json b/contracts/asset-proxy/CHANGELOG.json index ae79f77158..b73d4e65cc 100644 --- a/contracts/asset-proxy/CHANGELOG.json +++ b/contracts/asset-proxy/CHANGELOG.json @@ -26,6 +26,10 @@ "note": "Reworked `KyberBridge`", "pr": 2683 }, + { + "note": "Added `CreamBridge`", + "pr": 2715 + }, { "note": "Added `ShellBridge`", "pr": 2722 diff --git a/contracts/asset-proxy/contracts/src/bridges/CreamBridge.sol b/contracts/asset-proxy/contracts/src/bridges/CreamBridge.sol new file mode 100644 index 0000000000..c1a5750afa --- /dev/null +++ b/contracts/asset-proxy/contracts/src/bridges/CreamBridge.sol @@ -0,0 +1,103 @@ + +/* + + Copyright 2020 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity ^0.5.9; +pragma experimental ABIEncoderV2; + +import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; +import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol"; +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"; + + +contract CreamBridge is + IERC20Bridge, + IWallet, + DeploymentConstants +{ + /// @dev Callback for `IERC20Bridge`. Tries to buy `amount` of + /// `toTokenAddress` tokens by selling the entirety of the `fromTokenAddress` + /// token encoded in the bridge data, then transfers the bought + /// tokens to `to`. + /// @param toTokenAddress The token to buy and transfer to `to`. + /// @param from The maker (this contract). + /// @param to The recipient of the bought tokens. + /// @param amount Minimum amount of `toTokenAddress` tokens to buy. + /// @param bridgeData The abi-encoded addresses of the "from" token and Balancer pool. + /// @return success The magic bytes if successful. + function bridgeTransferFrom( + address toTokenAddress, + address from, + address to, + uint256 amount, + bytes calldata bridgeData + ) + external + returns (bytes4 success) + { + // Decode the bridge data. + (address fromTokenAddress, address poolAddress) = abi.decode( + bridgeData, + (address, address) + ); + require(toTokenAddress != fromTokenAddress, "CreamBridge/INVALID_PAIR"); + + uint256 fromTokenBalance = IERC20Token(fromTokenAddress).balanceOf(address(this)); + // Grant an allowance to the exchange to spend `fromTokenAddress` token. + LibERC20Token.approveIfBelow(fromTokenAddress, poolAddress, fromTokenBalance); + + // Sell all of this contract's `fromTokenAddress` token balance. + (uint256 boughtAmount,) = IBalancerPool(poolAddress).swapExactAmountIn( + fromTokenAddress, // tokenIn + fromTokenBalance, // tokenAmountIn + toTokenAddress, // tokenOut + amount, // minAmountOut + uint256(-1) // maxPrice + ); + + // Transfer the converted `toToken`s to `to`. + LibERC20Token.transfer(toTokenAddress, to, boughtAmount); + + emit ERC20BridgeTransfer( + fromTokenAddress, + toTokenAddress, + fromTokenBalance, + boughtAmount, + from, + to + ); + return BRIDGE_SUCCESS; + } + + /// @dev `SignatureType.Wallet` callback, so that this bridge can be the maker + /// and sign for itself in orders. Always succeeds. + /// @return magicValue Magic success bytes, always. + function isValidSignature( + bytes32, + bytes calldata + ) + external + view + returns (bytes4 magicValue) + { + return LEGACY_WALLET_MAGIC_VALUE; + } +} diff --git a/contracts/asset-proxy/package.json b/contracts/asset-proxy/package.json index 8ec798b923..0f8b6adff2 100644 --- a/contracts/asset-proxy/package.json +++ b/contracts/asset-proxy/package.json @@ -38,7 +38,7 @@ "docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES" }, "config": { - "abis": "./test/generated-artifacts/@(BalancerBridge|BancorBridge|ChaiBridge|CurveBridge|DODOBridge|DexForwarderBridge|DydxBridge|ERC1155Proxy|ERC20BridgeProxy|ERC20Proxy|ERC721Proxy|Eth2DaiBridge|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|IBalancerPool|IBancorNetwork|IChai|ICurve|IDydx|IDydxBridge|IERC20Bridge|IEth2Dai|IGasToken|IKyberNetworkProxy|IMStable|IMooniswap|IShell|IUniswapExchange|IUniswapExchangeFactory|IUniswapV2Router01|KyberBridge|MStableBridge|MixinAssetProxyDispatcher|MixinAuthorizable|MixinGasToken|MooniswapBridge|MultiAssetProxy|Ownable|ShellBridge|StaticCallProxy|SushiSwapBridge|TestBancorBridge|TestChaiBridge|TestDexForwarderBridge|TestDydxBridge|TestERC20Bridge|TestEth2DaiBridge|TestKyberBridge|TestStaticCallTarget|TestUniswapBridge|TestUniswapV2Bridge|UniswapBridge|UniswapV2Bridge).json", + "abis": "./test/generated-artifacts/@(BalancerBridge|BancorBridge|ChaiBridge|CreamBridge|CurveBridge|DODOBridge|DexForwarderBridge|DydxBridge|ERC1155Proxy|ERC20BridgeProxy|ERC20Proxy|ERC721Proxy|Eth2DaiBridge|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|IBalancerPool|IBancorNetwork|IChai|ICurve|IDydx|IDydxBridge|IERC20Bridge|IEth2Dai|IGasToken|IKyberNetworkProxy|IMStable|IMooniswap|IShell|IUniswapExchange|IUniswapExchangeFactory|IUniswapV2Router01|KyberBridge|MStableBridge|MixinAssetProxyDispatcher|MixinAuthorizable|MixinGasToken|MooniswapBridge|MultiAssetProxy|Ownable|ShellBridge|StaticCallProxy|SushiSwapBridge|TestBancorBridge|TestChaiBridge|TestDexForwarderBridge|TestDydxBridge|TestERC20Bridge|TestEth2DaiBridge|TestKyberBridge|TestStaticCallTarget|TestUniswapBridge|TestUniswapV2Bridge|UniswapBridge|UniswapV2Bridge).json", "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually." }, "repository": { diff --git a/contracts/asset-proxy/src/artifacts.ts b/contracts/asset-proxy/src/artifacts.ts index bfa82f8639..c2c15f58cf 100644 --- a/contracts/asset-proxy/src/artifacts.ts +++ b/contracts/asset-proxy/src/artifacts.ts @@ -8,6 +8,7 @@ import { ContractArtifact } from 'ethereum-types'; import * as BalancerBridge from '../generated-artifacts/BalancerBridge.json'; import * as BancorBridge from '../generated-artifacts/BancorBridge.json'; import * as ChaiBridge from '../generated-artifacts/ChaiBridge.json'; +import * as CreamBridge from '../generated-artifacts/CreamBridge.json'; import * as CurveBridge from '../generated-artifacts/CurveBridge.json'; import * as DexForwarderBridge from '../generated-artifacts/DexForwarderBridge.json'; import * as DODOBridge from '../generated-artifacts/DODOBridge.json'; @@ -73,6 +74,7 @@ export const artifacts = { BalancerBridge: BalancerBridge as ContractArtifact, BancorBridge: BancorBridge as ContractArtifact, ChaiBridge: ChaiBridge as ContractArtifact, + CreamBridge: CreamBridge as ContractArtifact, CurveBridge: CurveBridge as ContractArtifact, DODOBridge: DODOBridge as ContractArtifact, DexForwarderBridge: DexForwarderBridge as ContractArtifact, diff --git a/contracts/asset-proxy/src/wrappers.ts b/contracts/asset-proxy/src/wrappers.ts index f77fbaa312..8ba6a7b9a3 100644 --- a/contracts/asset-proxy/src/wrappers.ts +++ b/contracts/asset-proxy/src/wrappers.ts @@ -6,6 +6,7 @@ export * from '../generated-wrappers/balancer_bridge'; export * from '../generated-wrappers/bancor_bridge'; export * from '../generated-wrappers/chai_bridge'; +export * from '../generated-wrappers/cream_bridge'; export * from '../generated-wrappers/curve_bridge'; export * from '../generated-wrappers/d_o_d_o_bridge'; export * from '../generated-wrappers/dex_forwarder_bridge'; diff --git a/contracts/asset-proxy/test/artifacts.ts b/contracts/asset-proxy/test/artifacts.ts index 2ac620f6ed..1877c6425f 100644 --- a/contracts/asset-proxy/test/artifacts.ts +++ b/contracts/asset-proxy/test/artifacts.ts @@ -8,6 +8,7 @@ import { ContractArtifact } from 'ethereum-types'; import * as BalancerBridge from '../test/generated-artifacts/BalancerBridge.json'; import * as BancorBridge from '../test/generated-artifacts/BancorBridge.json'; import * as ChaiBridge from '../test/generated-artifacts/ChaiBridge.json'; +import * as CreamBridge from '../test/generated-artifacts/CreamBridge.json'; import * as CurveBridge from '../test/generated-artifacts/CurveBridge.json'; import * as DexForwarderBridge from '../test/generated-artifacts/DexForwarderBridge.json'; import * as DODOBridge from '../test/generated-artifacts/DODOBridge.json'; @@ -73,6 +74,7 @@ export const artifacts = { BalancerBridge: BalancerBridge as ContractArtifact, BancorBridge: BancorBridge as ContractArtifact, ChaiBridge: ChaiBridge as ContractArtifact, + CreamBridge: CreamBridge as ContractArtifact, CurveBridge: CurveBridge as ContractArtifact, DODOBridge: DODOBridge as ContractArtifact, DexForwarderBridge: DexForwarderBridge as ContractArtifact, diff --git a/contracts/asset-proxy/test/wrappers.ts b/contracts/asset-proxy/test/wrappers.ts index ba46bffc0c..4e2b808df5 100644 --- a/contracts/asset-proxy/test/wrappers.ts +++ b/contracts/asset-proxy/test/wrappers.ts @@ -6,6 +6,7 @@ export * from '../test/generated-wrappers/balancer_bridge'; export * from '../test/generated-wrappers/bancor_bridge'; export * from '../test/generated-wrappers/chai_bridge'; +export * from '../test/generated-wrappers/cream_bridge'; export * from '../test/generated-wrappers/curve_bridge'; export * from '../test/generated-wrappers/d_o_d_o_bridge'; export * from '../test/generated-wrappers/dex_forwarder_bridge'; diff --git a/contracts/asset-proxy/tsconfig.json b/contracts/asset-proxy/tsconfig.json index 22e9fc6eca..eaf2d7fd45 100644 --- a/contracts/asset-proxy/tsconfig.json +++ b/contracts/asset-proxy/tsconfig.json @@ -6,6 +6,7 @@ "generated-artifacts/BalancerBridge.json", "generated-artifacts/BancorBridge.json", "generated-artifacts/ChaiBridge.json", + "generated-artifacts/CreamBridge.json", "generated-artifacts/CurveBridge.json", "generated-artifacts/DODOBridge.json", "generated-artifacts/DexForwarderBridge.json", @@ -61,6 +62,7 @@ "test/generated-artifacts/BalancerBridge.json", "test/generated-artifacts/BancorBridge.json", "test/generated-artifacts/ChaiBridge.json", + "test/generated-artifacts/CreamBridge.json", "test/generated-artifacts/CurveBridge.json", "test/generated-artifacts/DODOBridge.json", "test/generated-artifacts/DexForwarderBridge.json", diff --git a/contracts/zero-ex/CHANGELOG.json b/contracts/zero-ex/CHANGELOG.json index 2bf7c0f902..56153459ab 100644 --- a/contracts/zero-ex/CHANGELOG.json +++ b/contracts/zero-ex/CHANGELOG.json @@ -65,6 +65,10 @@ { "note": "Added `Shell` into FQT", "pr": 2722 + }, + { + "note": "Added `CREAM` into FQT", + "pr": 2715 } ] }, diff --git a/contracts/zero-ex/contracts/src/transformers/bridges/BridgeAdapter.sol b/contracts/zero-ex/contracts/src/transformers/bridges/BridgeAdapter.sol index 761ec15da1..bedd77c898 100644 --- a/contracts/zero-ex/contracts/src/transformers/bridges/BridgeAdapter.sol +++ b/contracts/zero-ex/contracts/src/transformers/bridges/BridgeAdapter.sol @@ -46,6 +46,7 @@ contract BridgeAdapter is { address private immutable BALANCER_BRIDGE_ADDRESS; + address private immutable CREAM_BRIDGE_ADDRESS; address private immutable CURVE_BRIDGE_ADDRESS; address private immutable KYBER_BRIDGE_ADDRESS; address private immutable MOONISWAP_BRIDGE_ADDRESS; @@ -93,6 +94,7 @@ contract BridgeAdapter is SHELL_BRIDGE_ADDRESS = addresses.shellBridge; UNISWAP_BRIDGE_ADDRESS = addresses.uniswapBridge; UNISWAP_V2_BRIDGE_ADDRESS = addresses.uniswapV2Bridge; + CREAM_BRIDGE_ADDRESS = addresses.creamBridge; } function trade( @@ -134,7 +136,7 @@ contract BridgeAdapter is sellAmount, bridgeData ); - } else if (bridgeAddress == BALANCER_BRIDGE_ADDRESS) { + } else if (bridgeAddress == BALANCER_BRIDGE_ADDRESS || bridgeAddress == CREAM_BRIDGE_ADDRESS) { boughtAmount = _tradeBalancer( buyToken, sellAmount, diff --git a/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinAdapterAddresses.sol b/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinAdapterAddresses.sol index c9d13f5731..33e2de618d 100644 --- a/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinAdapterAddresses.sol +++ b/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinAdapterAddresses.sol @@ -24,6 +24,7 @@ contract MixinAdapterAddresses struct AdapterAddresses { // Bridges address balancerBridge; + address creamBridge; address curveBridge; address kyberBridge; address mooniswapBridge; diff --git a/contracts/zero-ex/test/transformers/fill_quote_transformer_test.ts b/contracts/zero-ex/test/transformers/fill_quote_transformer_test.ts index fbeffdf643..724688da44 100644 --- a/contracts/zero-ex/test/transformers/fill_quote_transformer_test.ts +++ b/contracts/zero-ex/test/transformers/fill_quote_transformer_test.ts @@ -75,6 +75,7 @@ blockchainTests.resets('FillQuoteTransformer', env => { weth: NULL_ADDRESS, shellBridge: NULL_ADDRESS, shell: NULL_ADDRESS, + creamBridge: NULL_ADDRESS, }, ); transformer = await FillQuoteTransformerContract.deployFrom0xArtifactAsync( diff --git a/packages/asset-swapper/package.json b/packages/asset-swapper/package.json index fc3707c3f3..bce6d04b7f 100644 --- a/packages/asset-swapper/package.json +++ b/packages/asset-swapper/package.json @@ -71,8 +71,13 @@ "@0x/web3-wrapper": "^7.2.0", "@balancer-labs/sor": "0.3.2", "@bancor/sdk": "^0.2.9", + "@ethersproject/abi": "^5.0.1", + "@ethersproject/address": "^5.0.1", + "@ethersproject/contracts": "^5.0.1", + "@ethersproject/providers": "^5.0.4", "axios": "^0.19.2", "axios-mock-adapter": "^1.18.1", + "cream-sor": "^0.3.3", "decimal.js": "^10.2.0", "ethereum-types": "^3.2.0", "ethereumjs-util": "^5.1.1", 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 8c2f570e25..1bb895690c 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/constants.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/constants.ts @@ -25,6 +25,7 @@ export const SELL_SOURCE_FILTER = new SourceFilters([ ERC20BridgeSource.Shell, ERC20BridgeSource.MultiHop, ERC20BridgeSource.Dodo, + ERC20BridgeSource.Cream, ]); /** @@ -47,6 +48,7 @@ export const BUY_SOURCE_FILTER = new SourceFilters( ERC20BridgeSource.SushiSwap, ERC20BridgeSource.MultiHop, ERC20BridgeSource.Dodo, + ERC20BridgeSource.Cream, ], [ERC20BridgeSource.MultiBridge], ); diff --git a/packages/asset-swapper/src/utils/market_operation_utils/cream_utils.ts b/packages/asset-swapper/src/utils/market_operation_utils/cream_utils.ts new file mode 100644 index 0000000000..a31e420b22 --- /dev/null +++ b/packages/asset-swapper/src/utils/market_operation_utils/cream_utils.ts @@ -0,0 +1,104 @@ +import { getPoolsWithTokens, parsePoolData } from 'cream-sor'; + +import { BalancerPool } from './balancer_utils'; + +// tslint:disable:boolean-naming + +interface CacheValue { + timestamp: number; + pools: BalancerPool[]; +} + +// tslint:disable:custom-no-magic-numbers +const FIVE_SECONDS_MS = 5 * 1000; +const ONE_DAY_MS = 24 * 60 * 60 * 1000; +const DEFAULT_TIMEOUT_MS = 1000; +const MAX_POOLS_FETCHED = 3; +// tslint:enable:custom-no-magic-numbers + +export class CreamPoolsCache { + constructor( + private readonly _cache: { [key: string]: CacheValue } = {}, + private readonly maxPoolsFetched: number = MAX_POOLS_FETCHED, + ) {} + + public async getPoolsForPairAsync( + takerToken: string, + makerToken: string, + timeoutMs: number = DEFAULT_TIMEOUT_MS, + ): Promise { + const timeout = new Promise(resolve => setTimeout(resolve, timeoutMs, [])); + return Promise.race([this._getPoolsForPairAsync(takerToken, makerToken), timeout]); + } + + public getCachedPoolAddressesForPair( + takerToken: string, + makerToken: string, + cacheExpiryMs?: number, + ): string[] | undefined { + const key = JSON.stringify([takerToken, makerToken]); + const value = this._cache[key]; + if (cacheExpiryMs === undefined) { + return value === undefined ? [] : value.pools.map(pool => pool.id); + } + const minTimestamp = Date.now() - cacheExpiryMs; + if (value === undefined || value.timestamp < minTimestamp) { + return undefined; + } else { + return value.pools.map(pool => pool.id); + } + } + + public howToSampleCream( + takerToken: string, + makerToken: string, + isAllowedSource: boolean, + ): { onChain: boolean; offChain: boolean } { + // If CREAM is excluded as a source, do not sample. + if (!isAllowedSource) { + return { onChain: false, offChain: false }; + } + const cachedCreamPools = this.getCachedPoolAddressesForPair(takerToken, makerToken, ONE_DAY_MS); + // Sample CREAM on-chain (i.e. via the ERC20BridgeSampler contract) if: + // - Cached values are not stale + // - There is at least one CREAM pool for this pair + const onChain = cachedCreamPools !== undefined && cachedCreamPools.length > 0; + // Sample CREAM off-chain (i.e. via GraphQL query + `computeCreamBuy/SellQuote`) + // if cached values are stale + const offChain = cachedCreamPools === undefined; + return { onChain, offChain }; + } + + protected async _getPoolsForPairAsync( + takerToken: string, + makerToken: string, + cacheExpiryMs: number = FIVE_SECONDS_MS, + ): Promise { + const key = JSON.stringify([takerToken, makerToken]); + const value = this._cache[key]; + const minTimestamp = Date.now() - cacheExpiryMs; + if (value === undefined || value.timestamp < minTimestamp) { + const pools = await this._fetchPoolsForPairAsync(takerToken, makerToken); + const timestamp = Date.now(); + this._cache[key] = { + pools, + timestamp, + }; + } + return this._cache[key].pools; + } + + // tslint:disable-next-line:prefer-function-over-method + protected async _fetchPoolsForPairAsync(takerToken: string, makerToken: string): Promise { + try { + const poolData = (await getPoolsWithTokens(takerToken, makerToken)).pools; + // Sort by maker token balance (descending) + const pools = parsePoolData(poolData, takerToken, makerToken).sort((a, b) => + b.balanceOut.minus(a.balanceOut).toNumber(), + ); + return pools.length > this.maxPoolsFetched ? pools.slice(0, this.maxPoolsFetched) : pools; + } catch (err) { + return []; + } + } +} diff --git a/packages/asset-swapper/src/utils/market_operation_utils/index.ts b/packages/asset-swapper/src/utils/market_operation_utils/index.ts index f0003ae259..c054959378 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/index.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/index.ts @@ -152,6 +152,20 @@ export class MarketOperationUtils { quoteSourceFilters.isAllowed(ERC20BridgeSource.Balancer), ); + const { + onChain: sampleCreamOnChain, + offChain: sampleCreamOffChain, + } = this._sampler.creamPoolsCache.howToSampleCream( + takerToken, + makerToken, + quoteSourceFilters.isAllowed(ERC20BridgeSource.Cream), + ); + + const offChainSources = [ + ...(!sampleCreamOnChain ? [ERC20BridgeSource.Cream] : []), + ...(!sampleBalancerOnChain ? [ERC20BridgeSource.Balancer] : []), + ]; + // Call the sampler contract. const samplerPromise = this._sampler.executeAsync( // Get native order fillable amounts. @@ -178,7 +192,7 @@ export class MarketOperationUtils { ), // Get sell quotes for taker -> maker. this._sampler.getSellQuotes( - quoteSourceFilters.exclude(sampleBalancerOnChain ? [] : ERC20BridgeSource.Balancer).sources, + quoteSourceFilters.exclude(offChainSources).sources, makerToken, takerToken, sampleAmounts, @@ -211,6 +225,10 @@ export class MarketOperationUtils { ? this._sampler.getBalancerSellQuotesOffChainAsync(makerToken, takerToken, sampleAmounts) : Promise.resolve([]); + const offChainCreamPromise = sampleCreamOffChain + ? this._sampler.getCreamSellQuotesOffChainAsync(makerToken, takerToken, sampleAmounts) + : Promise.resolve([]); + const offChainBancorPromise = quoteSourceFilters.isAllowed(ERC20BridgeSource.Bancor) ? this._sampler.getBancorSellQuotesOffChainAsync(makerToken, takerToken, [takerAmount]) : Promise.resolve([]); @@ -219,15 +237,22 @@ export class MarketOperationUtils { [orderFillableAmounts, ethToMakerAssetRate, ethToTakerAssetRate, dexQuotes, twoHopQuotes], rfqtIndicativeQuotes, offChainBalancerQuotes, + offChainCreamQuotes, offChainBancorQuotes, - ] = await Promise.all([samplerPromise, rfqtPromise, offChainBalancerPromise, offChainBancorPromise]); + ] = await Promise.all([ + samplerPromise, + rfqtPromise, + offChainBalancerPromise, + offChainCreamPromise, + offChainBancorPromise, + ]); return { side: MarketOperation.Sell, inputAmount: takerAmount, inputToken: takerToken, outputToken: makerToken, - dexQuotes: dexQuotes.concat([...offChainBalancerQuotes, offChainBancorQuotes]), + dexQuotes: dexQuotes.concat([...offChainBalancerQuotes, ...offChainCreamQuotes, offChainBancorQuotes]), nativeOrders, orderFillableAmounts, ethToOutputRate: ethToMakerAssetRate, @@ -270,6 +295,20 @@ export class MarketOperationUtils { quoteSourceFilters.isAllowed(ERC20BridgeSource.Balancer), ); + const { + onChain: sampleCreamOnChain, + offChain: sampleCreamOffChain, + } = this._sampler.creamPoolsCache.howToSampleCream( + takerToken, + makerToken, + quoteSourceFilters.isAllowed(ERC20BridgeSource.Cream), + ); + + const offChainSources = [ + ...(!sampleCreamOnChain ? [ERC20BridgeSource.Cream] : []), + ...(!sampleBalancerOnChain ? [ERC20BridgeSource.Balancer] : []), + ]; + // Call the sampler contract. const samplerPromise = this._sampler.executeAsync( // Get native order fillable amounts. @@ -296,7 +335,7 @@ export class MarketOperationUtils { ), // Get buy quotes for taker -> maker. this._sampler.getBuyQuotes( - quoteSourceFilters.exclude(sampleBalancerOnChain ? [] : ERC20BridgeSource.Balancer).sources, + quoteSourceFilters.exclude(offChainSources).sources, makerToken, takerToken, sampleAmounts, @@ -328,11 +367,16 @@ export class MarketOperationUtils { ? this._sampler.getBalancerBuyQuotesOffChainAsync(makerToken, takerToken, sampleAmounts) : Promise.resolve([]); + const offChainCreamPromise = sampleCreamOffChain + ? this._sampler.getCreamBuyQuotesOffChainAsync(makerToken, takerToken, sampleAmounts) + : Promise.resolve([]); + const [ [orderFillableAmounts, ethToMakerAssetRate, ethToTakerAssetRate, dexQuotes, twoHopQuotes], rfqtIndicativeQuotes, offChainBalancerQuotes, - ] = await Promise.all([samplerPromise, rfqtPromise, offChainBalancerPromise]); + offChainCreamQuotes, + ] = await Promise.all([samplerPromise, rfqtPromise, offChainBalancerPromise, offChainCreamPromise]); // Attach the MultiBridge address to the sample fillData (dexQuotes.find(quotes => quotes[0] && quotes[0].source === ERC20BridgeSource.MultiBridge) || []).forEach( q => (q.fillData = { poolAddress: this._multiBridge }), @@ -342,7 +386,7 @@ export class MarketOperationUtils { inputAmount: makerAmount, inputToken: makerToken, outputToken: takerToken, - dexQuotes: dexQuotes.concat(offChainBalancerQuotes), + dexQuotes: dexQuotes.concat(offChainBalancerQuotes, offChainCreamQuotes), nativeOrders, orderFillableAmounts, ethToOutputRate: ethToTakerAssetRate, 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 e08353d8d7..e06b815718 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/orders.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/orders.ts @@ -177,6 +177,8 @@ function getBridgeAddressFromFill(fill: CollapsedFill, opts: CreateOrderFromPath return opts.contractAddresses.bancorBridge; case ERC20BridgeSource.Balancer: return opts.contractAddresses.balancerBridge; + case ERC20BridgeSource.Cream: + return opts.contractAddresses.creamBridge; case ERC20BridgeSource.LiquidityProvider: return (fill.fillData as LiquidityProviderFillData).poolAddress; case ERC20BridgeSource.MultiBridge: @@ -241,6 +243,14 @@ export function createBridgeOrder( createBalancerBridgeData(takerToken, balancerFillData.poolAddress), ); break; + case ERC20BridgeSource.Cream: + const creamFillData = (fill as CollapsedFill).fillData!; // tslint:disable-line:no-non-null-assertion + makerAssetData = assetDataUtils.encodeERC20BridgeAssetData( + makerToken, + bridgeAddress, + createBalancerBridgeData(takerToken, creamFillData.poolAddress), + ); + break; case ERC20BridgeSource.Bancor: const bancorFillData = (fill as CollapsedFill).fillData!; // tslint:disable-line:no-non-null-assertion makerAssetData = assetDataUtils.encodeERC20BridgeAssetData( diff --git a/packages/asset-swapper/src/utils/market_operation_utils/sampler.ts b/packages/asset-swapper/src/utils/market_operation_utils/sampler.ts index d17bbd6831..5c6f0cc1db 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/sampler.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/sampler.ts @@ -6,6 +6,7 @@ import { ERC20BridgeSamplerContract } from '../../wrappers'; import { BalancerPoolsCache } from './balancer_utils'; import { BancorService } from './bancor_service'; +import { CreamPoolsCache } from './cream_utils'; import { SamplerOperations } from './sampler_operations'; import { BatchedOperation } from './types'; @@ -37,9 +38,10 @@ export class DexOrderSampler extends SamplerOperations { private readonly _samplerOverrides?: SamplerOverrides, provider?: SupportedProvider, balancerPoolsCache?: BalancerPoolsCache, + creamPoolsCache?: CreamPoolsCache, getBancorServiceFn?: () => BancorService, ) { - super(_samplerContract, provider, balancerPoolsCache, getBancorServiceFn); + super(_samplerContract, provider, balancerPoolsCache, creamPoolsCache, getBancorServiceFn); } /* Type overloads for `executeAsync()`. Could skip this if we would upgrade TS. */ 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 3ef05d0c94..5d5b0385a8 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 @@ -8,6 +8,7 @@ import { ERC20BridgeSamplerContract } from '../../wrappers'; import { BalancerPoolsCache, computeBalancerBuyQuote, computeBalancerSellQuote } from './balancer_utils'; import { BancorService } from './bancor_service'; import { MAINNET_SUSHI_SWAP_ROUTER, MAX_UINT256, NULL_BYTES, ZERO_AMOUNT } from './constants'; +import { CreamPoolsCache } from './cream_utils'; import { getCurveInfosForPair, getSwerveInfosForPair } from './curve_utils'; import { getKyberReserveIdsForPair } from './kyber_utils'; import { getMultiBridgeIntermediateToken } from './multibridge_utils'; @@ -73,6 +74,7 @@ export class SamplerOperations { protected readonly _samplerContract: ERC20BridgeSamplerContract, public readonly provider?: SupportedProvider, public readonly balancerPoolsCache: BalancerPoolsCache = new BalancerPoolsCache(), + public readonly creamPoolsCache: CreamPoolsCache = new CreamPoolsCache(), protected readonly getBancorServiceFn?: () => BancorService, // for dependency injection in tests ) {} @@ -410,9 +412,10 @@ export class SamplerOperations { makerToken: string, takerToken: string, takerFillAmounts: BigNumber[], + source: ERC20BridgeSource, ): SourceQuoteOperation { return new SamplerContractOperation({ - source: ERC20BridgeSource.Balancer, + source, fillData: { poolAddress }, contract: this._samplerContract, function: this._samplerContract.sampleSellsFromBalancer, @@ -425,9 +428,10 @@ export class SamplerOperations { makerToken: string, takerToken: string, makerFillAmounts: BigNumber[], + source: ERC20BridgeSource, ): SourceQuoteOperation { return new SamplerContractOperation({ - source: ERC20BridgeSource.Balancer, + source, fillData: { poolAddress }, contract: this._samplerContract, function: this._samplerContract.sampleBuysFromBalancer, @@ -467,6 +471,38 @@ export class SamplerOperations { ); } + public async getCreamSellQuotesOffChainAsync( + makerToken: string, + takerToken: string, + takerFillAmounts: BigNumber[], + ): Promise>>> { + const pools = await this.creamPoolsCache.getPoolsForPairAsync(takerToken, makerToken); + return pools.map(pool => + takerFillAmounts.map(amount => ({ + source: ERC20BridgeSource.Cream, + output: computeBalancerSellQuote(pool, amount), + input: amount, + fillData: { poolAddress: pool.id }, + })), + ); + } + + public async getCreamBuyQuotesOffChainAsync( + makerToken: string, + takerToken: string, + makerFillAmounts: BigNumber[], + ): Promise>>> { + const pools = await this.creamPoolsCache.getPoolsForPairAsync(takerToken, makerToken); + return pools.map(pool => + makerFillAmounts.map(amount => ({ + source: ERC20BridgeSource.Cream, + output: computeBalancerBuyQuote(pool, amount), + input: amount, + fillData: { poolAddress: pool.id }, + })), + ); + } + public getMStableSellQuotes( makerToken: string, takerToken: string, @@ -1039,7 +1075,25 @@ export class SamplerOperations { return this.balancerPoolsCache .getCachedPoolAddressesForPair(takerToken, makerToken)! .map(poolAddress => - this.getBalancerSellQuotes(poolAddress, makerToken, takerToken, takerFillAmounts), + this.getBalancerSellQuotes( + poolAddress, + makerToken, + takerToken, + takerFillAmounts, + ERC20BridgeSource.Balancer, + ), + ); + case ERC20BridgeSource.Cream: + return this.creamPoolsCache + .getCachedPoolAddressesForPair(takerToken, makerToken)! + .map(poolAddress => + this.getBalancerSellQuotes( + poolAddress, + makerToken, + takerToken, + takerFillAmounts, + ERC20BridgeSource.Cream, + ), ); case ERC20BridgeSource.Shell: return this.getShellSellQuotes(makerToken, takerToken, takerFillAmounts); @@ -1130,7 +1184,25 @@ export class SamplerOperations { return this.balancerPoolsCache .getCachedPoolAddressesForPair(takerToken, makerToken)! .map(poolAddress => - this.getBalancerBuyQuotes(poolAddress, makerToken, takerToken, makerFillAmounts), + this.getBalancerBuyQuotes( + poolAddress, + makerToken, + takerToken, + makerFillAmounts, + ERC20BridgeSource.Balancer, + ), + ); + case ERC20BridgeSource.Cream: + return this.creamPoolsCache + .getCachedPoolAddressesForPair(takerToken, makerToken)! + .map(poolAddress => + this.getBalancerBuyQuotes( + poolAddress, + makerToken, + takerToken, + makerFillAmounts, + ERC20BridgeSource.Cream, + ), ); case ERC20BridgeSource.Shell: return this.getShellBuyQuotes(makerToken, takerToken, makerFillAmounts); diff --git a/packages/asset-swapper/src/utils/market_operation_utils/types.ts b/packages/asset-swapper/src/utils/market_operation_utils/types.ts index 7d526defbc..5a7ebc9dc4 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/types.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/types.ts @@ -37,6 +37,7 @@ export enum ERC20BridgeSource { LiquidityProvider = 'LiquidityProvider', MultiBridge = 'MultiBridge', Balancer = 'Balancer', + Cream = 'CREAM', Bancor = 'Bancor', MStable = 'mStable', Mooniswap = 'Mooniswap', diff --git a/packages/asset-swapper/test/dex_sampler_test.ts b/packages/asset-swapper/test/dex_sampler_test.ts index b267e0d170..8d2706b0e3 100644 --- a/packages/asset-swapper/test/dex_sampler_test.ts +++ b/packages/asset-swapper/test/dex_sampler_test.ts @@ -504,6 +504,7 @@ describe('DexSampler tests', () => { undefined, // sampler overrides provider, undefined, // balancer cache + undefined, // cream cache () => bancorService, ); const quotes = await dexOrderSampler.getBancorSellQuotesOffChainAsync( diff --git a/packages/asset-swapper/test/market_operation_utils_test.ts b/packages/asset-swapper/test/market_operation_utils_test.ts index 0fb1bd35b6..8e54b748fa 100644 --- a/packages/asset-swapper/test/market_operation_utils_test.ts +++ b/packages/asset-swapper/test/market_operation_utils_test.ts @@ -25,6 +25,7 @@ import { SOURCE_FLAGS, ZERO_AMOUNT, } from '../src/utils/market_operation_utils/constants'; +import { CreamPoolsCache } from '../src/utils/market_operation_utils/cream_utils'; import { createFills } from '../src/utils/market_operation_utils/fills'; import { DexOrderSampler } from '../src/utils/market_operation_utils/sampler'; import { BATCH_SOURCE_FILTERS } from '../src/utils/market_operation_utils/sampler_operations'; @@ -45,6 +46,7 @@ const DEFAULT_EXCLUDED = [ ERC20BridgeSource.SushiSwap, ERC20BridgeSource.MultiHop, ERC20BridgeSource.Shell, + ERC20BridgeSource.Cream, ERC20BridgeSource.Dodo, ]; const BUY_SOURCES = BUY_SOURCE_FILTER.sources; @@ -285,6 +287,7 @@ describe('MarketOperationUtils tests', () => { [ERC20BridgeSource.SushiSwap]: _.times(NUM_SAMPLES, () => 0), [ERC20BridgeSource.MultiHop]: _.times(NUM_SAMPLES, () => 0), [ERC20BridgeSource.Shell]: _.times(NUM_SAMPLES, () => 0), + [ERC20BridgeSource.Cream]: _.times(NUM_SAMPLES, () => 0), [ERC20BridgeSource.Dodo]: _.times(NUM_SAMPLES, () => 0), }; @@ -332,6 +335,7 @@ describe('MarketOperationUtils tests', () => { [ERC20BridgeSource.Native]: { order: createOrder() }, [ERC20BridgeSource.MultiHop]: {}, [ERC20BridgeSource.Shell]: {}, + [ERC20BridgeSource.Cream]: { poolAddress: randomAddress() }, [ERC20BridgeSource.Dodo]: {}, }; @@ -369,6 +373,22 @@ describe('MarketOperationUtils tests', () => { DEFAULT_FILL_DATA[ERC20BridgeSource.Balancer], ), ], + getCreamSellQuotesOffChainAsync: (_makerToken: string, _takerToken: string, takerFillAmounts: BigNumber[]) => [ + createSamplesFromRates( + ERC20BridgeSource.Cream, + takerFillAmounts, + createDecreasingRates(takerFillAmounts.length), + DEFAULT_FILL_DATA[ERC20BridgeSource.Cream], + ), + ], + getCreamBuyQuotesOffChainAsync: (_makerToken: string, _takerToken: string, makerFillAmounts: BigNumber[]) => [ + createSamplesFromRates( + ERC20BridgeSource.Cream, + makerFillAmounts, + createDecreasingRates(makerFillAmounts.length).map(r => new BigNumber(1).div(r)), + DEFAULT_FILL_DATA[ERC20BridgeSource.Cream], + ), + ], getBancorSellQuotesOffChainAsync: (_makerToken: string, _takerToken: string, takerFillAmounts: BigNumber[]) => createSamplesFromRates( ERC20BridgeSource.Bancor, @@ -388,6 +408,7 @@ describe('MarketOperationUtils tests', () => { return ops; }, balancerPoolsCache: new BalancerPoolsCache(), + creamPoolsCache: new CreamPoolsCache(), } as any) as DexOrderSampler; function replaceSamplerOps(ops: Partial = {}): void { @@ -491,6 +512,14 @@ describe('MarketOperationUtils tests', () => { sourcesPolled = sourcesPolled.concat(ERC20BridgeSource.Balancer); return DEFAULT_OPS.getBalancerSellQuotesOffChainAsync(makerToken, takerToken, takerFillAmounts); }, + getCreamSellQuotesOffChainAsync: ( + makerToken: string, + takerToken: string, + takerFillAmounts: BigNumber[], + ) => { + sourcesPolled = sourcesPolled.concat(ERC20BridgeSource.Cream); + return DEFAULT_OPS.getCreamSellQuotesOffChainAsync(makerToken, takerToken, takerFillAmounts); + }, }); await marketOperationUtils.getMarketSellOrdersAsync(ORDERS, FILL_AMOUNT, { ...DEFAULT_OPTS, @@ -521,6 +550,14 @@ describe('MarketOperationUtils tests', () => { args.sources = args.sources.concat(ERC20BridgeSource.Balancer); return DEFAULT_OPS.getBalancerSellQuotesOffChainAsync(makerToken, takerToken, takerFillAmounts); }, + getCreamSellQuotesOffChainAsync: ( + makerToken: string, + takerToken: string, + takerFillAmounts: BigNumber[], + ) => { + args.sources = args.sources.concat(ERC20BridgeSource.Cream); + return DEFAULT_OPS.getCreamSellQuotesOffChainAsync(makerToken, takerToken, takerFillAmounts); + }, }); const registryAddress = randomAddress(); const newMarketOperationUtils = new MarketOperationUtils( @@ -562,6 +599,14 @@ describe('MarketOperationUtils tests', () => { sourcesPolled = sourcesPolled.concat(ERC20BridgeSource.Balancer); return DEFAULT_OPS.getBalancerSellQuotesOffChainAsync(makerToken, takerToken, takerFillAmounts); }, + getCreamSellQuotesOffChainAsync: ( + makerToken: string, + takerToken: string, + takerFillAmounts: BigNumber[], + ) => { + sourcesPolled = sourcesPolled.concat(ERC20BridgeSource.Cream); + return DEFAULT_OPS.getCreamSellQuotesOffChainAsync(makerToken, takerToken, takerFillAmounts); + }, }); await marketOperationUtils.getMarketSellOrdersAsync(ORDERS, FILL_AMOUNT, { ...DEFAULT_OPTS, @@ -593,6 +638,14 @@ describe('MarketOperationUtils tests', () => { sourcesPolled = sourcesPolled.concat(ERC20BridgeSource.Balancer); return DEFAULT_OPS.getBalancerSellQuotesOffChainAsync(makerToken, takerToken, takerFillAmounts); }, + getCreamSellQuotesOffChainAsync: ( + makerToken: string, + takerToken: string, + takerFillAmounts: BigNumber[], + ) => { + sourcesPolled = sourcesPolled.concat(ERC20BridgeSource.Cream); + return DEFAULT_OPS.getCreamSellQuotesOffChainAsync(makerToken, takerToken, takerFillAmounts); + }, }); await marketOperationUtils.getMarketSellOrdersAsync(ORDERS, FILL_AMOUNT, { ...DEFAULT_OPTS, @@ -983,6 +1036,14 @@ describe('MarketOperationUtils tests', () => { sourcesPolled = sourcesPolled.concat(ERC20BridgeSource.Balancer); return DEFAULT_OPS.getBalancerBuyQuotesOffChainAsync(makerToken, takerToken, makerFillAmounts); }, + getCreamBuyQuotesOffChainAsync: ( + makerToken: string, + takerToken: string, + makerFillAmounts: BigNumber[], + ) => { + sourcesPolled = sourcesPolled.concat(ERC20BridgeSource.Cream); + return DEFAULT_OPS.getCreamBuyQuotesOffChainAsync(makerToken, takerToken, makerFillAmounts); + }, }); await marketOperationUtils.getMarketBuyOrdersAsync(ORDERS, FILL_AMOUNT, { ...DEFAULT_OPTS, @@ -1013,6 +1074,14 @@ describe('MarketOperationUtils tests', () => { args.sources = args.sources.concat(ERC20BridgeSource.Balancer); return DEFAULT_OPS.getBalancerBuyQuotesOffChainAsync(makerToken, takerToken, makerFillAmounts); }, + getCreamBuyQuotesOffChainAsync: ( + makerToken: string, + takerToken: string, + makerFillAmounts: BigNumber[], + ) => { + args.sources = args.sources.concat(ERC20BridgeSource.Cream); + return DEFAULT_OPS.getCreamBuyQuotesOffChainAsync(makerToken, takerToken, makerFillAmounts); + }, }); const registryAddress = randomAddress(); const newMarketOperationUtils = new MarketOperationUtils( @@ -1054,6 +1123,14 @@ describe('MarketOperationUtils tests', () => { sourcesPolled = sourcesPolled.concat(ERC20BridgeSource.Balancer); return DEFAULT_OPS.getBalancerBuyQuotesOffChainAsync(makerToken, takerToken, makerFillAmounts); }, + getCreamBuyQuotesOffChainAsync: ( + makerToken: string, + takerToken: string, + makerFillAmounts: BigNumber[], + ) => { + sourcesPolled = sourcesPolled.concat(ERC20BridgeSource.Cream); + return DEFAULT_OPS.getCreamBuyQuotesOffChainAsync(makerToken, takerToken, makerFillAmounts); + }, }); await marketOperationUtils.getMarketBuyOrdersAsync(ORDERS, FILL_AMOUNT, { ...DEFAULT_OPTS, @@ -1085,6 +1162,14 @@ describe('MarketOperationUtils tests', () => { sourcesPolled = sourcesPolled.concat(ERC20BridgeSource.Balancer); return DEFAULT_OPS.getBalancerBuyQuotesOffChainAsync(makerToken, takerToken, makerFillAmounts); }, + getCreamBuyQuotesOffChainAsync: ( + makerToken: string, + takerToken: string, + makerFillAmounts: BigNumber[], + ) => { + sourcesPolled = sourcesPolled.concat(ERC20BridgeSource.Cream); + return DEFAULT_OPS.getCreamBuyQuotesOffChainAsync(makerToken, takerToken, makerFillAmounts); + }, }); await marketOperationUtils.getMarketBuyOrdersAsync(ORDERS, FILL_AMOUNT, { ...DEFAULT_OPTS, diff --git a/packages/contract-addresses/CHANGELOG.json b/packages/contract-addresses/CHANGELOG.json index 5db5010744..757a5e2b8e 100644 --- a/packages/contract-addresses/CHANGELOG.json +++ b/packages/contract-addresses/CHANGELOG.json @@ -57,6 +57,10 @@ { "note": "Deploy `DodoBridge` on Mainnet", "pr": 2701 + }, + { + "note": "Deploy `CreamBridge` on Mainnet", + "pr": 2715 } ] }, diff --git a/packages/contract-addresses/addresses.json b/packages/contract-addresses/addresses.json index ddb87a0fea..80aad5c1c8 100644 --- a/packages/contract-addresses/addresses.json +++ b/packages/contract-addresses/addresses.json @@ -45,6 +45,7 @@ "sushiswapBridge": "0x47ed0262a0b688dcb836d254c6a2e96b6c48a9f5", "shellBridge": "0x21fb3862eed7911e0f8219a077247b849846728d", "dodoBridge": "0xe9da66965a9344aab2167e6813c03f043cc7a6ca", + "creamBridge": "0xb9d4bf2c8dab828f4ffb656acdb6c2b497d44f25", "transformers": { "wethTransformer": "0x68c0bb685099dc7cb5c5ce2b26185945b357383e", "payTakerTransformer": "0x49b9df2c58491764cf40cb052dd4243df63622c7", @@ -98,6 +99,7 @@ "sushiswapBridge": "0x0000000000000000000000000000000000000000", "shellBridge": "0x0000000000000000000000000000000000000000", "dodoBridge": "0x0000000000000000000000000000000000000000", + "creamBridge": "0x0000000000000000000000000000000000000000", "transformers": { "wethTransformer": "0x8d822fe2b42f60531203e288f5f357fa79474437", "payTakerTransformer": "0x150652244723102faeaefa4c79597d097ffa26c6", @@ -151,6 +153,7 @@ "sushiswapBridge": "0x0000000000000000000000000000000000000000", "shellBridge": "0x0000000000000000000000000000000000000000", "dodoBridge": "0x0000000000000000000000000000000000000000", + "creamBridge": "0x0000000000000000000000000000000000000000", "transformers": { "wethTransformer": "0x8d822fe2b42f60531203e288f5f357fa79474437", "payTakerTransformer": "0x150652244723102faeaefa4c79597d097ffa26c6", @@ -204,6 +207,7 @@ "sushiswapBridge": "0x0000000000000000000000000000000000000000", "shellBridge": "0x0000000000000000000000000000000000000000", "dodoBridge": "0x0000000000000000000000000000000000000000", + "creamBridge": "0x0000000000000000000000000000000000000000", "transformers": { "wethTransformer": "0x9ce35b5ee9e710535e3988e3f8731d9ca9dba17d", "payTakerTransformer": "0x5a53e7b02a83aa9f60ccf4e424f0442c255bc977", @@ -257,6 +261,7 @@ "sushiswapBridge": "0x0000000000000000000000000000000000000000", "shellBridge": "0x0000000000000000000000000000000000000000", "dodoBridge": "0x0000000000000000000000000000000000000000", + "creamBridge": "0x0000000000000000000000000000000000000000", "transformers": { "wethTransformer": "0xc6b0d3c45a6b5092808196cb00df5c357d55e1d5", "payTakerTransformer": "0x7209185959d7227fb77274e1e88151d7c4c368d3", diff --git a/packages/contract-addresses/src/index.ts b/packages/contract-addresses/src/index.ts index 3c45f86ce3..a2f7c6be04 100644 --- a/packages/contract-addresses/src/index.ts +++ b/packages/contract-addresses/src/index.ts @@ -46,6 +46,7 @@ export interface ContractAddresses { sushiswapBridge: string; shellBridge: string; dodoBridge: string; + creamBridge: string; transformers: { wethTransformer: string; payTakerTransformer: string; diff --git a/packages/migrations/src/migration.ts b/packages/migrations/src/migration.ts index a7a07f9f39..542eb7786f 100644 --- a/packages/migrations/src/migration.ts +++ b/packages/migrations/src/migration.ts @@ -325,6 +325,7 @@ export async function runMigrationsAsync( uniswapExchangeFactory: NULL_ADDRESS, mStable: NULL_ADDRESS, shellBridge: NULL_ADDRESS, + creamBridge: NULL_ADDRESS, shell: NULL_ADDRESS, weth: etherToken.address, }, @@ -405,6 +406,7 @@ export async function runMigrationsAsync( sushiswapBridge: NULL_ADDRESS, shellBridge: NULL_ADDRESS, dodoBridge: NULL_ADDRESS, + creamBridge: NULL_ADDRESS, exchangeProxy: exchangeProxy.address, exchangeProxyAllowanceTarget: exchangeProxyAllowanceTargetAddress, exchangeProxyTransformerDeployer: txDefaults.from, diff --git a/yarn.lock b/yarn.lock index f5a89efc2b..c98c05e5d2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1093,6 +1093,242 @@ version "0.7.2" resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.2.tgz#b180097bb44a40c3b59efea9cf28df2fe542d518" +"@ethersproject/abi@^5.0.1", "@ethersproject/abi@^5.0.5": + version "5.0.7" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.0.7.tgz#79e52452bd3ca2956d0e1c964207a58ad1a0ee7b" + integrity sha512-Cqktk+hSIckwP/W8O47Eef60VwmoSC/L3lY0+dIBhQPCNn9E4V7rwmm2aFrNRRDJfFlGuZ1khkQUOc3oBX+niw== + dependencies: + "@ethersproject/address" "^5.0.4" + "@ethersproject/bignumber" "^5.0.7" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/constants" "^5.0.4" + "@ethersproject/hash" "^5.0.4" + "@ethersproject/keccak256" "^5.0.3" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/properties" "^5.0.3" + "@ethersproject/strings" "^5.0.4" + +"@ethersproject/abstract-provider@^5.0.4": + version "5.0.5" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.0.5.tgz#797a32a8707830af1ad8f833e9c228994d5572b9" + integrity sha512-i/CjElAkzV7vQBAeoz+IpjGfcFYEP9eD7j3fzZ0fzTq03DO7PPnR+xkEZ1IoDXGwDS+55aLM1xvLDwB/Lx6IOQ== + dependencies: + "@ethersproject/bignumber" "^5.0.7" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/networks" "^5.0.3" + "@ethersproject/properties" "^5.0.3" + "@ethersproject/transactions" "^5.0.5" + "@ethersproject/web" "^5.0.6" + +"@ethersproject/abstract-signer@^5.0.4": + version "5.0.6" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.0.6.tgz#c01211665ab9c9e93988c4783b789712fd93a388" + integrity sha512-h8TZBX3pL2Xx9tmsRxfWcaaI+FcJFHWvZ/vNvFjLp8zJ0kPD501LKTt2jo44LZ20N3EW68JMoyEmRQ6bpsn+iA== + dependencies: + "@ethersproject/abstract-provider" "^5.0.4" + "@ethersproject/bignumber" "^5.0.7" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/properties" "^5.0.3" + +"@ethersproject/address@^5.0.1", "@ethersproject/address@^5.0.4": + version "5.0.5" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.0.5.tgz#2caa65f6b7125015395b1b54c985ee0b27059cc7" + integrity sha512-DpkQ6rwk9jTefrRsJzEm6nhRiJd9pvhn1xN0rw5N/jswXG5r7BLk/GVA0mMAVWAsYfvi2xSc5L41FMox43RYEA== + dependencies: + "@ethersproject/bignumber" "^5.0.7" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/keccak256" "^5.0.3" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/rlp" "^5.0.3" + bn.js "^4.4.0" + +"@ethersproject/base64@^5.0.3": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.0.4.tgz#b0d8fdbf3dda977cf546dcd35725a7b1d5256caa" + integrity sha512-4KRykQ7BQMeOXfvio1YITwHjxwBzh92UoXIdzxDE1p53CK28bbHPdsPNYo0wl0El7lJAMpT2SOdL0hhbWRnyIA== + dependencies: + "@ethersproject/bytes" "^5.0.4" + +"@ethersproject/basex@^5.0.3": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.0.4.tgz#93e1cd11f9a47281da2389de24f88e13e9d90847" + integrity sha512-ixIr/kKiAoSzOnSc777AGIOAhKai5Ivqr4HO/Gz+YG+xkfv6kqD6AW4ga9vM20Wwb0QBhh3LoRWTu4V1K+x9Ew== + dependencies: + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/properties" "^5.0.3" + +"@ethersproject/bignumber@^5.0.7": + version "5.0.8" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.0.8.tgz#cee33bd8eb0266176def0d371b45274b1d2c4ec0" + integrity sha512-KXFVAFKS1jdTXYN8BE5Oj+ZfPMh28iRdFeNGBVT6cUFdtiPVqeXqc0ggvBqA3A1VoFFGgM7oAeaagA393aORHA== + dependencies: + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + bn.js "^4.4.0" + +"@ethersproject/bytes@^5.0.4": + version "5.0.5" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.0.5.tgz#688b70000e550de0c97a151a21f15b87d7f97d7c" + integrity sha512-IEj9HpZB+ACS6cZ+QQMTqmu/cnUK2fYNE6ms/PVxjoBjoxc6HCraLpam1KuRvreMy0i523PLmjN8OYeikRdcUQ== + dependencies: + "@ethersproject/logger" "^5.0.5" + +"@ethersproject/constants@^5.0.4": + version "5.0.5" + resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.0.5.tgz#0ed19b002e8404bdf6d135234dc86a7d9bcf9b71" + integrity sha512-foaQVmxp2+ik9FrLUCtVrLZCj4M3Ibgkqvh+Xw/vFRSerkjVSYePApaVE5essxhoSlF1U9oXfWY09QI2AXtgKA== + dependencies: + "@ethersproject/bignumber" "^5.0.7" + +"@ethersproject/contracts@^5.0.1": + version "5.0.5" + resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.0.5.tgz#64831a341ec8ca225e83ff3e9437c26b970fd5d7" + integrity sha512-tFI255lFbmbqMkgnuyhDWHl3yWqttPlReplYuVvDCT/SuvBjLR4ad2uipBlh1fh5X1ipK9ettAoV4S0HKim4Kw== + dependencies: + "@ethersproject/abi" "^5.0.5" + "@ethersproject/abstract-provider" "^5.0.4" + "@ethersproject/abstract-signer" "^5.0.4" + "@ethersproject/address" "^5.0.4" + "@ethersproject/bignumber" "^5.0.7" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/constants" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/properties" "^5.0.3" + +"@ethersproject/hash@^5.0.4": + version "5.0.5" + resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.0.5.tgz#e383ba2c7941834266fa6e2cf543d2b0c50a9d59" + integrity sha512-GpI80/h2HDpfNKpCZoxQJCjOQloGnlD5hM1G+tZe8FQDJhEvFjJoPDuWv+NaYjJfOciKS2Axqc4Q4WamdLoUgg== + dependencies: + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/keccak256" "^5.0.3" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/strings" "^5.0.4" + +"@ethersproject/keccak256@^5.0.3": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.0.4.tgz#36ca0a7d1ae2a272da5654cb886776d0c680ef3a" + integrity sha512-GNpiOUm9PGUxFNqOxYKDQBM0u68bG9XC9iOulEQ8I0tOx/4qUpgVzvgXL6ugxr0RY554Gz/NQsVqknqPzUcxpQ== + dependencies: + "@ethersproject/bytes" "^5.0.4" + js-sha3 "0.5.7" + +"@ethersproject/logger@^5.0.5": + version "5.0.6" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.0.6.tgz#faa484203e86e08be9e07fef826afeef7183fe88" + integrity sha512-FrX0Vnb3JZ1md/7GIZfmJ06XOAA8r3q9Uqt9O5orr4ZiksnbpXKlyDzQtlZ5Yv18RS8CAUbiKH9vwidJg1BPmQ== + +"@ethersproject/networks@^5.0.3": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.0.4.tgz#6d320a5e15a0cda804f5da88be0ba846156f6eec" + integrity sha512-/wHDTRms5mpJ09BoDrbNdFWINzONe05wZRgohCXvEv39rrH/Gd/yAnct8wC0RsW3tmFOgjgQxuBvypIxuUynTw== + dependencies: + "@ethersproject/logger" "^5.0.5" + +"@ethersproject/properties@^5.0.3": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.0.4.tgz#a67a1f5a52c30850b5062c861631e73d131f666e" + integrity sha512-UdyX3GqBxFt15B0uSESdDNmhvEbK3ACdDXl2soshoPcneXuTswHDeA0LoPlnaZzhbgk4p6jqb4GMms5C26Qu6A== + dependencies: + "@ethersproject/logger" "^5.0.5" + +"@ethersproject/providers@^5.0.4": + version "5.0.12" + resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.0.12.tgz#de05e865e130709ea1e0870511eb892bda7d41cb" + integrity sha512-bRUEVNth+wGlm2Q0cQprVlixBWumfP9anrgAc3V2CbIh+GKvCwisVO8uRLrZOfOvTNSy6PUJi/Z4D5L+k3NAog== + dependencies: + "@ethersproject/abstract-provider" "^5.0.4" + "@ethersproject/abstract-signer" "^5.0.4" + "@ethersproject/address" "^5.0.4" + "@ethersproject/basex" "^5.0.3" + "@ethersproject/bignumber" "^5.0.7" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/constants" "^5.0.4" + "@ethersproject/hash" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/networks" "^5.0.3" + "@ethersproject/properties" "^5.0.3" + "@ethersproject/random" "^5.0.3" + "@ethersproject/rlp" "^5.0.3" + "@ethersproject/sha2" "^5.0.3" + "@ethersproject/strings" "^5.0.4" + "@ethersproject/transactions" "^5.0.5" + "@ethersproject/web" "^5.0.6" + bech32 "1.1.4" + ws "7.2.3" + +"@ethersproject/random@^5.0.3": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.0.4.tgz#98f7cf65b0e588cec39ef24843e391ed5004556f" + integrity sha512-AIZJhqs6Ba4/+U3lOjt3QZbP6b/kuuGLJUYFUonAgWmkTHwqsCwYnFvnHKQSUuHbXHvErp7WFXFlztx+yMn3kQ== + dependencies: + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + +"@ethersproject/rlp@^5.0.3": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.0.4.tgz#0090a0271e84ea803016a112a79f5cfd80271a77" + integrity sha512-5qrrZad7VTjofxSsm7Zg/7Dr4ZOln4S2CqiDdOuTv6MBKnXj0CiBojXyuDy52M8O3wxH0CyE924hXWTDV1PQWQ== + dependencies: + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + +"@ethersproject/sha2@^5.0.3": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.0.4.tgz#40f639721a27dbe034b3dee021ba20b054586fec" + integrity sha512-0yFhf1mspxAfWdXXoPtK94adUeu1R7/FzAa+DfEiZTc76sz/vHXf0LSIazoR3znYKFny6haBxME+usbvvEcF3A== + dependencies: + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + hash.js "1.1.3" + +"@ethersproject/signing-key@^5.0.4": + version "5.0.5" + resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.0.5.tgz#acfd06fc05a14180df7e027688bbd23fc4baf782" + integrity sha512-Z1wY7JC1HVO4CvQWY2TyTTuAr8xK3bJijZw1a9G92JEmKdv1j255R/0YLBBcFTl2J65LUjtXynNJ2GbArPGi5g== + dependencies: + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/properties" "^5.0.3" + elliptic "6.5.3" + +"@ethersproject/strings@^5.0.4": + version "5.0.5" + resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.0.5.tgz#ed7e99a282a02f40757691b04a24cd83f3752195" + integrity sha512-JED6WaIV00xM/gvj8vSnd+0VWtDYdidTmavFRCTQakqfz+4tDo6Jz5LHgG+dd45h7ah7ykCHW0C7ZXWEDROCXQ== + dependencies: + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/constants" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + +"@ethersproject/transactions@^5.0.5": + version "5.0.6" + resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.0.6.tgz#b8b27938be6e9ed671dbdd35fe98af8b14d0df7c" + integrity sha512-htsFhOD+NMBxx676A8ehSuwVV49iqpSB+CkjPZ02tpNew0K6p8g0CZ46Z1ZP946gIHAU80xQ0NACHYrjIUaCFA== + dependencies: + "@ethersproject/address" "^5.0.4" + "@ethersproject/bignumber" "^5.0.7" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/constants" "^5.0.4" + "@ethersproject/keccak256" "^5.0.3" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/properties" "^5.0.3" + "@ethersproject/rlp" "^5.0.3" + "@ethersproject/signing-key" "^5.0.4" + +"@ethersproject/web@^5.0.6": + version "5.0.9" + resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.0.9.tgz#b08f8295f4bfd4777c8723fe9572f5453b9f03cb" + integrity sha512-//QNlv1MSkOII1hv3+HQwWoiVFS+BMVGI0KYeUww4cyrEktnx1QIez5bTSab9s9fWTFaWKNmQNBwMbxAqPuYDw== + dependencies: + "@ethersproject/base64" "^5.0.3" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/properties" "^5.0.3" + "@ethersproject/strings" "^5.0.4" + "@evocateur/libnpmaccess@^3.1.2": version "3.1.2" resolved "https://registry.yarnpkg.com/@evocateur/libnpmaccess/-/libnpmaccess-3.1.2.tgz#ecf7f6ce6b004e9f942b098d92200be4a4b1c845" @@ -3911,6 +4147,11 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" +bech32@1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" + integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ== + before-after-hook@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.1.0.tgz#b6c03487f44e24200dd30ca5e6a1979c5d2fb635" @@ -5510,6 +5751,14 @@ coveralls@^3.0.0: minimist "^1.2.0" request "^2.79.0" +cream-sor@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/cream-sor/-/cream-sor-0.3.3.tgz#ae7ab50c68cfd36a89e2101187ceebbb79e1b14c" + integrity sha512-taQSvCUunPhwyebwbjxh1l9NDp39lsre+Q2oRMK+gqzbu+Wlbg5GAquwoV2/GLgzia70gi4x1rJCJsUmc5Kygg== + dependencies: + bignumber.js "^9.0.0" + isomorphic-fetch "^2.2.1" + create-ecdh@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.1.tgz#44223dfed533193ba5ba54e0df5709b89acf1f82" @@ -6356,6 +6605,19 @@ elliptic@6.5.2: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.0" +elliptic@6.5.3: + version "6.5.3" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.3.tgz#cb59eb2efdaf73a0bd78ccd7015a62ad6e0f93d6" + integrity sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw== + dependencies: + bn.js "^4.4.0" + brorand "^1.0.1" + hash.js "^1.0.0" + hmac-drbg "^1.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.0" + elliptic@^6.0.0, elliptic@^6.2.3, elliptic@^6.4.0: version "6.4.0" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df" @@ -18441,6 +18703,11 @@ write@^0.2.1: dependencies: mkdirp "^0.5.1" +ws@7.2.3: + version "7.2.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.2.3.tgz#a5411e1fb04d5ed0efee76d26d5c46d830c39b46" + integrity sha512-HTDl9G9hbkNDk98naoR/cHDws7+EyYMOdL1BmjsZXRUjf7d+MficC4B7HLUPlSiho0vg+CWKrGIt/VJBd1xunQ== + ws@^3.0.0: version "3.3.3" resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2"