diff --git a/contracts/asset-proxy/contracts/src/bridges/CryptoComBridge.sol b/contracts/asset-proxy/contracts/src/bridges/CryptoComBridge.sol new file mode 100644 index 0000000000..3be8a4b80e --- /dev/null +++ b/contracts/asset-proxy/contracts/src/bridges/CryptoComBridge.sol @@ -0,0 +1,136 @@ +/* + + 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/interfaces/IEtherToken.sol"; +import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol"; +import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol"; +import "@0x/contracts-utils/contracts/src/LibAddressArray.sol"; +import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol"; +import "../interfaces/IUniswapV2Router01.sol"; +import "../interfaces/IERC20Bridge.sol"; + + +// solhint-disable space-after-comma +// solhint-disable not-rely-on-time +contract CryptoComBridge is + IERC20Bridge, + IWallet, + DeploymentConstants +{ + struct TransferState { + address[] path; + address router; + uint256 fromTokenBalance; + } + + /// @dev Callback for `IERC20Bridge`. Tries to buy `amount` of + /// `toTokenAddress` tokens by selling the entirety of the `fromTokenAddress` + /// token encoded in the bridge data. + /// @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 path of token addresses. Last element must be toTokenAddress + /// @return success The magic bytes if successful. + function bridgeTransferFrom( + address toTokenAddress, + address from, + address to, + uint256 amount, + bytes calldata bridgeData + ) + external + returns (bytes4 success) + { + // hold variables to get around stack depth limitations + TransferState memory state; + + // Decode the bridge data to get the `fromTokenAddress`. + // solhint-disable indent + (state.path, state.router) = abi.decode(bridgeData, (address[], address)); + // solhint-enable indent + + require(state.path.length >= 2, "CryptoComBridge/PATH_LENGTH_MUST_BE_AT_LEAST_TWO"); + require(state.path[state.path.length - 1] == toTokenAddress, "CryptoComBridge/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN"); + + // Just transfer the tokens if they're the same. + if (state.path[0] == toTokenAddress) { + LibERC20Token.transfer(state.path[0], to, amount); + return BRIDGE_SUCCESS; + } + + // Get our balance of `fromTokenAddress` token. + state.fromTokenBalance = IERC20Token(state.path[0]).balanceOf(address(this)); + + // Grant the SushiSwap router an allowance. + LibERC20Token.approveIfBelow( + state.path[0], + state.router, + state.fromTokenBalance + ); + + // Buy as much `toTokenAddress` token with `fromTokenAddress` token + // and transfer it to `to`. + IUniswapV2Router01 router = IUniswapV2Router01(state.router); + uint[] memory amounts = router.swapExactTokensForTokens( + // Sell all tokens we hold. + state.fromTokenBalance, + // Minimum buy amount. + amount, + // Convert `fromTokenAddress` to `toTokenAddress`. + state.path, + // Recipient is `to`. + to, + // Expires after this block. + block.timestamp + ); + + emit ERC20BridgeTransfer( + // input token + state.path[0], + // output token + toTokenAddress, + // input token amount + state.fromTokenBalance, + // output token amount + amounts[amounts.length - 1], + 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 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 e809932dca..077db85208 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|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|SnowSwapBridge|StaticCallProxy|SushiSwapBridge|SwerveBridge|TestBancorBridge|TestChaiBridge|TestDexForwarderBridge|TestDydxBridge|TestERC20Bridge|TestEth2DaiBridge|TestKyberBridge|TestStaticCallTarget|TestUniswapBridge|TestUniswapV2Bridge|UniswapBridge|UniswapV2Bridge).json", + "abis": "./test/generated-artifacts/@(BalancerBridge|BancorBridge|ChaiBridge|CreamBridge|CryptoComBridge|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|SnowSwapBridge|StaticCallProxy|SushiSwapBridge|SwerveBridge|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 16515d5ab6..00b95798dc 100644 --- a/contracts/asset-proxy/src/artifacts.ts +++ b/contracts/asset-proxy/src/artifacts.ts @@ -9,6 +9,7 @@ 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 CryptoComBridge from '../generated-artifacts/CryptoComBridge.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'; @@ -77,6 +78,7 @@ export const artifacts = { BancorBridge: BancorBridge as ContractArtifact, ChaiBridge: ChaiBridge as ContractArtifact, CreamBridge: CreamBridge as ContractArtifact, + CryptoComBridge: CryptoComBridge 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 76df73fb0e..e51d91096e 100644 --- a/contracts/asset-proxy/src/wrappers.ts +++ b/contracts/asset-proxy/src/wrappers.ts @@ -7,6 +7,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/crypto_com_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 cc6dff48cc..2ccc9699e8 100644 --- a/contracts/asset-proxy/test/artifacts.ts +++ b/contracts/asset-proxy/test/artifacts.ts @@ -9,6 +9,7 @@ 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 CryptoComBridge from '../test/generated-artifacts/CryptoComBridge.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'; @@ -77,6 +78,7 @@ export const artifacts = { BancorBridge: BancorBridge as ContractArtifact, ChaiBridge: ChaiBridge as ContractArtifact, CreamBridge: CreamBridge as ContractArtifact, + CryptoComBridge: CryptoComBridge 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 8c511d62f1..e289f39f64 100644 --- a/contracts/asset-proxy/test/wrappers.ts +++ b/contracts/asset-proxy/test/wrappers.ts @@ -7,6 +7,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/crypto_com_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 b3d8d879e0..e6ee5945f4 100644 --- a/contracts/asset-proxy/tsconfig.json +++ b/contracts/asset-proxy/tsconfig.json @@ -7,6 +7,7 @@ "generated-artifacts/BancorBridge.json", "generated-artifacts/ChaiBridge.json", "generated-artifacts/CreamBridge.json", + "generated-artifacts/CryptoComBridge.json", "generated-artifacts/CurveBridge.json", "generated-artifacts/DODOBridge.json", "generated-artifacts/DexForwarderBridge.json", @@ -65,6 +66,7 @@ "test/generated-artifacts/BancorBridge.json", "test/generated-artifacts/ChaiBridge.json", "test/generated-artifacts/CreamBridge.json", + "test/generated-artifacts/CryptoComBridge.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 7b7120ddef..9152a23c53 100644 --- a/contracts/zero-ex/CHANGELOG.json +++ b/contracts/zero-ex/CHANGELOG.json @@ -53,6 +53,10 @@ { "note": "Add a permissionless transformer deployer", "pr": 55 + }, + { + "note": "Add Crypto.com to `BridgeAdapter`", + "pr": 43 } ] }, diff --git a/contracts/zero-ex/contracts/src/transformers/bridges/BridgeAdapter.sol b/contracts/zero-ex/contracts/src/transformers/bridges/BridgeAdapter.sol index bb92419f74..18f1dbf281 100644 --- a/contracts/zero-ex/contracts/src/transformers/bridges/BridgeAdapter.sol +++ b/contracts/zero-ex/contracts/src/transformers/bridges/BridgeAdapter.sol @@ -22,6 +22,7 @@ pragma experimental ABIEncoderV2; import "./mixins/MixinAdapterAddresses.sol"; import "./mixins/MixinBalancer.sol"; import "./mixins/MixinCurve.sol"; +import "./mixins/MixinCryptoCom.sol"; import "./mixins/MixinDodo.sol"; import "./mixins/MixinKyber.sol"; import "./mixins/MixinMooniswap.sol"; @@ -37,6 +38,7 @@ contract BridgeAdapter is MixinAdapterAddresses, MixinBalancer, MixinCurve, + MixinCryptoCom, MixinDodo, MixinKyber, MixinMooniswap, @@ -52,6 +54,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 CRYPTO_COM_BRIDGE_ADDRESS; address private immutable DODO_BRIDGE_ADDRESS; address private immutable KYBER_BRIDGE_ADDRESS; address private immutable MOONISWAP_BRIDGE_ADDRESS; @@ -68,6 +71,7 @@ contract BridgeAdapter is public MixinBalancer() MixinCurve() + MixinCryptoCom(addresses) MixinDodo(addresses) MixinKyber(addresses) MixinMooniswap(addresses) @@ -81,6 +85,7 @@ contract BridgeAdapter is { BALANCER_BRIDGE_ADDRESS = addresses.balancerBridge; CURVE_BRIDGE_ADDRESS = addresses.curveBridge; + CRYPTO_COM_BRIDGE_ADDRESS = addresses.cryptoComBridge; KYBER_BRIDGE_ADDRESS = addresses.kyberBridge; MOONISWAP_BRIDGE_ADDRESS = addresses.mooniswapBridge; MSTABLE_BRIDGE_ADDRESS = addresses.mStableBridge; @@ -185,6 +190,12 @@ contract BridgeAdapter is sellAmount, bridgeData ); + } else if (bridgeAddress == CRYPTO_COM_BRIDGE_ADDRESS) { + boughtAmount = _tradeCryptoCom( + buyToken, + sellAmount, + bridgeData + ); } else { boughtAmount = _tradeZeroExBridge( bridgeAddress, 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 8595875b75..a3ec5eff27 100644 --- a/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinAdapterAddresses.sol +++ b/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinAdapterAddresses.sol @@ -26,6 +26,7 @@ contract MixinAdapterAddresses address balancerBridge; address creamBridge; address curveBridge; + address cryptoComBridge; address dodoBridge; address kyberBridge; address mooniswapBridge; diff --git a/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinCryptoCom.sol b/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinCryptoCom.sol new file mode 100644 index 0000000000..5f56560fa8 --- /dev/null +++ b/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinCryptoCom.sol @@ -0,0 +1,79 @@ + +/* + + 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.6.5; +pragma experimental ABIEncoderV2; + +import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol"; +import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol"; +import "./MixinAdapterAddresses.sol"; +import "./MixinUniswapV2.sol"; + +contract MixinCryptoCom is + MixinAdapterAddresses +{ + using LibERC20TokenV06 for IERC20TokenV06; + + /// @dev Mainnet address of the `CryptoComRouter` contract. + IUniswapV2Router02 private immutable CRYPTOCOM_ROUTER; + + constructor(AdapterAddresses memory addresses) + public + { + CRYPTOCOM_ROUTER = IUniswapV2Router02(addresses.cryptoComBridge); + } + + function _tradeCryptoCom( + IERC20TokenV06 buyToken, + uint256 sellAmount, + bytes memory bridgeData + ) + internal + returns (uint256 boughtAmount) + { + // solhint-disable indent + address[] memory path = abi.decode(bridgeData, (address[])); + // solhint-enable indent + + require(path.length >= 2, "CryptoComBridge/PATH_LENGTH_MUST_BE_AT_LEAST_TWO"); + require( + path[path.length - 1] == address(buyToken), + "CryptoComBridge/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN" + ); + // Grant the Uniswap router an allowance to sell the first token. + IERC20TokenV06(path[0]).approveIfBelow( + address(CRYPTOCOM_ROUTER), + sellAmount + ); + + uint[] memory amounts = CRYPTOCOM_ROUTER.swapExactTokensForTokens( + // Sell all tokens we hold. + sellAmount, + // Minimum buy amount. + 1, + // Convert to `buyToken` along this path. + path, + // Recipient is `this`. + address(this), + // Expires after this block. + block.timestamp + ); + return amounts[amounts.length-1]; + } +} diff --git a/contracts/zero-ex/package.json b/contracts/zero-ex/package.json index 60ab991288..0fdc327c0c 100644 --- a/contracts/zero-ex/package.json +++ b/contracts/zero-ex/package.json @@ -42,7 +42,7 @@ "config": { "publicInterfaceContracts": "IZeroEx,ZeroEx,FullMigration,InitialMigration,IFlashWallet,IAllowanceTarget,IERC20Transformer,IOwnableFeature,ISimpleFunctionRegistryFeature,ITokenSpenderFeature,ITransformERC20Feature,FillQuoteTransformer,PayTakerTransformer,WethTransformer,OwnableFeature,SimpleFunctionRegistryFeature,TransformERC20Feature,TokenSpenderFeature,AffiliateFeeTransformer,SignatureValidatorFeature,MetaTransactionsFeature,LogMetadataTransformer,BridgeAdapter,LiquidityProviderFeature,ILiquidityProviderFeature,NativeOrdersFeature,INativeOrdersFeature", "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.", - "abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|AllowanceTarget|BootstrapFeature|BridgeAdapter|FeeCollector|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinProtocolFees|FixinReentrancyGuard|FixinTokenSpender|FlashWallet|FullMigration|IAllowanceTarget|IBootstrapFeature|IBridgeAdapter|IERC20Bridge|IERC20Transformer|IExchange|IFeature|IFlashWallet|IGasToken|ILiquidityProvider|ILiquidityProviderFeature|ILiquidityProviderSandbox|IMetaTransactionsFeature|INativeOrdersFeature|IOwnableFeature|ISignatureValidatorFeature|ISimpleFunctionRegistryFeature|IStaking|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibLiquidityProviderRichErrors|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibNativeOrder|LibNativeOrdersRichErrors|LibNativeOrdersStorage|LibOrderHash|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignature|LibSignatureRichErrors|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibSpenderRichErrors|LibStorage|LibTokenSpenderStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LiquidityProviderSandbox|LogMetadataTransformer|MetaTransactionsFeature|MixinAdapterAddresses|MixinBalancer|MixinCurve|MixinDodo|MixinKyber|MixinMStable|MixinMooniswap|MixinOasis|MixinShell|MixinSushiswap|MixinUniswap|MixinUniswapV2|MixinZeroExBridge|NativeOrdersFeature|OwnableFeature|PayTakerTransformer|PermissionlessTransformerDeployer|SignatureValidatorFeature|SimpleFunctionRegistryFeature|TestBridge|TestCallTarget|TestDelegateCaller|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFixinProtocolFees|TestFixinTokenSpender|TestFullMigration|TestInitialMigration|TestLibNativeOrder|TestLibSignature|TestLiquidityProvider|TestMetaTransactionsNativeOrdersFeature|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestNativeOrdersFeature|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|TestRfqOriginRegistration|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpender|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestWeth|TestWethTransformerHost|TestZeroExFeature|TokenSpenderFeature|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|WethTransformer|ZeroEx|ZeroExOptimized).json" + "abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|AllowanceTarget|BootstrapFeature|BridgeAdapter|FeeCollector|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinProtocolFees|FixinReentrancyGuard|FixinTokenSpender|FlashWallet|FullMigration|IAllowanceTarget|IBootstrapFeature|IBridgeAdapter|IERC20Bridge|IERC20Transformer|IExchange|IFeature|IFlashWallet|IGasToken|ILiquidityProvider|ILiquidityProviderFeature|ILiquidityProviderSandbox|IMetaTransactionsFeature|INativeOrdersFeature|IOwnableFeature|ISignatureValidatorFeature|ISimpleFunctionRegistryFeature|IStaking|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibLiquidityProviderRichErrors|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibNativeOrder|LibNativeOrdersRichErrors|LibNativeOrdersStorage|LibOrderHash|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignature|LibSignatureRichErrors|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibSpenderRichErrors|LibStorage|LibTokenSpenderStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LiquidityProviderSandbox|LogMetadataTransformer|MetaTransactionsFeature|MixinAdapterAddresses|MixinBalancer|MixinCryptoCom|MixinCurve|MixinDodo|MixinKyber|MixinMStable|MixinMooniswap|MixinOasis|MixinShell|MixinSushiswap|MixinUniswap|MixinUniswapV2|MixinZeroExBridge|NativeOrdersFeature|OwnableFeature|PayTakerTransformer|PermissionlessTransformerDeployer|SignatureValidatorFeature|SimpleFunctionRegistryFeature|TestBridge|TestCallTarget|TestDelegateCaller|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFixinProtocolFees|TestFixinTokenSpender|TestFullMigration|TestInitialMigration|TestLibNativeOrder|TestLibSignature|TestLiquidityProvider|TestMetaTransactionsNativeOrdersFeature|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestNativeOrdersFeature|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|TestRfqOriginRegistration|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpender|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestWeth|TestWethTransformerHost|TestZeroExFeature|TokenSpenderFeature|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|WethTransformer|ZeroEx|ZeroExOptimized).json" }, "repository": { "type": "git", diff --git a/contracts/zero-ex/test/artifacts.ts b/contracts/zero-ex/test/artifacts.ts index 8df2956935..39e6e02669 100644 --- a/contracts/zero-ex/test/artifacts.ts +++ b/contracts/zero-ex/test/artifacts.ts @@ -74,6 +74,7 @@ import * as LogMetadataTransformer from '../test/generated-artifacts/LogMetadata import * as MetaTransactionsFeature from '../test/generated-artifacts/MetaTransactionsFeature.json'; import * as MixinAdapterAddresses from '../test/generated-artifacts/MixinAdapterAddresses.json'; import * as MixinBalancer from '../test/generated-artifacts/MixinBalancer.json'; +import * as MixinCryptoCom from '../test/generated-artifacts/MixinCryptoCom.json'; import * as MixinCurve from '../test/generated-artifacts/MixinCurve.json'; import * as MixinDodo from '../test/generated-artifacts/MixinDodo.json'; import * as MixinKyber from '../test/generated-artifacts/MixinKyber.json'; @@ -210,6 +211,7 @@ export const artifacts = { IBridgeAdapter: IBridgeAdapter as ContractArtifact, MixinAdapterAddresses: MixinAdapterAddresses as ContractArtifact, MixinBalancer: MixinBalancer as ContractArtifact, + MixinCryptoCom: MixinCryptoCom as ContractArtifact, MixinCurve: MixinCurve as ContractArtifact, MixinDodo: MixinDodo as ContractArtifact, MixinKyber: MixinKyber as ContractArtifact, 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 70a4a7812d..1747aa22fa 100644 --- a/contracts/zero-ex/test/transformers/fill_quote_transformer_test.ts +++ b/contracts/zero-ex/test/transformers/fill_quote_transformer_test.ts @@ -81,6 +81,7 @@ blockchainTests.resets('FillQuoteTransformer', env => { dodoBridge: NULL_ADDRESS, dodoHelper: NULL_ADDRESS, snowSwapBridge: NULL_ADDRESS, + cryptoComBridge: NULL_ADDRESS, }, ); transformer = await FillQuoteTransformerContract.deployFrom0xArtifactAsync( diff --git a/contracts/zero-ex/test/wrappers.ts b/contracts/zero-ex/test/wrappers.ts index f39c8a14ca..6c4860e97c 100644 --- a/contracts/zero-ex/test/wrappers.ts +++ b/contracts/zero-ex/test/wrappers.ts @@ -72,6 +72,7 @@ export * from '../test/generated-wrappers/log_metadata_transformer'; export * from '../test/generated-wrappers/meta_transactions_feature'; export * from '../test/generated-wrappers/mixin_adapter_addresses'; export * from '../test/generated-wrappers/mixin_balancer'; +export * from '../test/generated-wrappers/mixin_crypto_com'; export * from '../test/generated-wrappers/mixin_curve'; export * from '../test/generated-wrappers/mixin_dodo'; export * from '../test/generated-wrappers/mixin_kyber'; diff --git a/contracts/zero-ex/tsconfig.json b/contracts/zero-ex/tsconfig.json index dddd09b753..8c53a7269b 100644 --- a/contracts/zero-ex/tsconfig.json +++ b/contracts/zero-ex/tsconfig.json @@ -99,6 +99,7 @@ "test/generated-artifacts/MetaTransactionsFeature.json", "test/generated-artifacts/MixinAdapterAddresses.json", "test/generated-artifacts/MixinBalancer.json", + "test/generated-artifacts/MixinCryptoCom.json", "test/generated-artifacts/MixinCurve.json", "test/generated-artifacts/MixinDodo.json", "test/generated-artifacts/MixinKyber.json", diff --git a/packages/asset-swapper/CHANGELOG.json b/packages/asset-swapper/CHANGELOG.json index b444ff1c26..12307187db 100644 --- a/packages/asset-swapper/CHANGELOG.json +++ b/packages/asset-swapper/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "5.3.0", + "changes": [ + { + "note": "Added Crypto.com", + "pr": 43 + } + ] + }, { "version": "5.2.0", "changes": [ diff --git a/packages/asset-swapper/src/types.ts b/packages/asset-swapper/src/types.ts index a5c6769390..095c3e5a3c 100644 --- a/packages/asset-swapper/src/types.ts +++ b/packages/asset-swapper/src/types.ts @@ -426,4 +426,5 @@ export interface BridgeContractAddresses { creamBridge: string; swerveBridge: string; snowswapBridge: string; + cryptoComBridge: string; } 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 7c5cd9e5c3..906ecae817 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/constants.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/constants.ts @@ -45,6 +45,7 @@ export const SELL_SOURCE_FILTER = new SourceFilters([ ERC20BridgeSource.Dodo, ERC20BridgeSource.Cream, ERC20BridgeSource.LiquidityProvider, + ERC20BridgeSource.CryptoCom, ]); /** @@ -69,6 +70,7 @@ export const BUY_SOURCE_FILTER = new SourceFilters([ ERC20BridgeSource.Dodo, ERC20BridgeSource.Cream, ERC20BridgeSource.LiquidityProvider, + ERC20BridgeSource.CryptoCom, ]); /** @@ -352,6 +354,7 @@ export const MAINNET_KYBER_TOKEN_RESERVE_IDS: { [token: string]: string } = { export const LIQUIDITY_PROVIDER_REGISTRY: LiquidityProviderRegistry = {}; export const MAINNET_SUSHI_SWAP_ROUTER = '0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F'; +export const MAINNET_CRYPTO_COM_ROUTER = '0xCeB90E4C17d626BE0fACd78b79c9c87d7ca181b3'; export const MAINNET_SHELL_POOLS = { StableCoins: { @@ -394,6 +397,7 @@ const EMPTY_BRIDGE_ADDRESSES: BridgeContractAddresses = { creamBridge: NULL_ADDRESS, snowswapBridge: NULL_ADDRESS, swerveBridge: NULL_ADDRESS, + cryptoComBridge: NULL_ADDRESS, }; export const BRIDGE_ADDRESSES_BY_CHAIN: { [chainId in ChainId]: BridgeContractAddresses } = { @@ -414,6 +418,7 @@ export const BRIDGE_ADDRESSES_BY_CHAIN: { [chainId in ChainId]: BridgeContractAd creamBridge: '0xb9d4bf2c8dab828f4ffb656acdb6c2b497d44f25', swerveBridge: '0xf9786d5eb1de47fa56a8f7bb387653c6d410bfee', snowswapBridge: '0xb1dbe83d15236ec10fdb214c6b89774b454754fd', + cryptoComBridge: '0x015850307f6aab4ac6631923ceefe71b57492c9b', }, [ChainId.Kovan]: { ...EMPTY_BRIDGE_ADDRESSES, @@ -483,7 +488,16 @@ export const DEFAULT_GAS_SCHEDULE: Required = { }, [ERC20BridgeSource.SushiSwap]: (fillData?: FillData) => { // TODO: Different base cost if to/from ETH. - let gas = 95e3; + let gas = 90e3; + const path = (fillData as SushiSwapFillData).tokenAddressPath; + if (path.length > 2) { + gas += (path.length - 2) * 60e3; // +60k for each hop. + } + return gas; + }, + [ERC20BridgeSource.CryptoCom]: (fillData?: FillData) => { + // TODO: Different base cost if to/from ETH. + let gas = 90e3 + 20e3 + 60e3; // temporary allowance diff, unrolled FQT const path = (fillData as SushiSwapFillData).tokenAddressPath; if (path.length > 2) { gas += (path.length - 2) * 60e3; // +60k for each hop. 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 6b8608d58b..8dac145d03 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/orders.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/orders.ts @@ -199,6 +199,8 @@ function getBridgeAddressFromFill(fill: CollapsedFill, opts: CreateOrderFromPath return opts.contractAddresses.shellBridge; case ERC20BridgeSource.Dodo: return opts.contractAddresses.dodoBridge; + case ERC20BridgeSource.CryptoCom: + return opts.contractAddresses.cryptoComBridge; default: break; } @@ -297,6 +299,14 @@ export function createBridgeOrder( createSushiSwapBridgeData(sushiSwapFillData.tokenAddressPath, sushiSwapFillData.router), ); break; + case ERC20BridgeSource.CryptoCom: + const cryptoComFillData = (fill as CollapsedFill).fillData!; // tslint:disable-line:no-non-null-assertion + makerAssetData = assetDataUtils.encodeERC20BridgeAssetData( + makerToken, + bridgeAddress, + createSushiSwapBridgeData(cryptoComFillData.tokenAddressPath, cryptoComFillData.router), + ); + break; case ERC20BridgeSource.Kyber: const kyberFillData = (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_operations.ts b/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts index 27f3bb9178..deb95c071e 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 @@ -7,7 +7,13 @@ import { ERC20BridgeSamplerContract } from '../../wrappers'; import { BalancerPoolsCache } from './balancer_utils'; import { BancorService } from './bancor_service'; -import { LIQUIDITY_PROVIDER_REGISTRY, MAINNET_SUSHI_SWAP_ROUTER, MAX_UINT256, ZERO_AMOUNT } from './constants'; +import { + LIQUIDITY_PROVIDER_REGISTRY, + MAINNET_CRYPTO_COM_ROUTER, + MAINNET_SUSHI_SWAP_ROUTER, + MAX_UINT256, + ZERO_AMOUNT, +} from './constants'; import { CreamPoolsCache } from './cream_utils'; import { getCurveInfosForPair, getSnowSwapInfosForPair, getSwerveInfosForPair } from './curve_utils'; import { getKyberReserveIdsForPair } from './kyber_utils'; @@ -789,6 +795,32 @@ export class SamplerOperations { }); } + public getCryptoComSellQuotes( + tokenAddressPath: string[], + takerFillAmounts: BigNumber[], + ): SourceQuoteOperation { + return new SamplerContractOperation({ + source: ERC20BridgeSource.CryptoCom, + fillData: { tokenAddressPath, router: MAINNET_CRYPTO_COM_ROUTER }, + contract: this._samplerContract, + function: this._samplerContract.sampleSellsFromSushiSwap, + params: [MAINNET_CRYPTO_COM_ROUTER, tokenAddressPath, takerFillAmounts], + }); + } + + public getCryptoComBuyQuotes( + tokenAddressPath: string[], + makerFillAmounts: BigNumber[], + ): SourceQuoteOperation { + return new SamplerContractOperation({ + source: ERC20BridgeSource.CryptoCom, + fillData: { tokenAddressPath, router: MAINNET_CRYPTO_COM_ROUTER }, + contract: this._samplerContract, + function: this._samplerContract.sampleBuysFromSushiSwap, + params: [MAINNET_CRYPTO_COM_ROUTER, tokenAddressPath, makerFillAmounts], + }); + } + public getShellSellQuotes( poolAddress: string, makerToken: string, @@ -993,6 +1025,16 @@ export class SamplerOperations { ); }); return sushiOps; + case ERC20BridgeSource.CryptoCom: + const cryptoComOps = [ + this.getCryptoComSellQuotes([takerToken, makerToken], takerFillAmounts), + ]; + intermediateTokens.forEach(t => { + cryptoComOps.push( + this.getCryptoComSellQuotes([takerToken, t, makerToken], takerFillAmounts), + ); + }); + return cryptoComOps; case ERC20BridgeSource.Kyber: return getKyberReserveIdsForPair(takerToken, makerToken).map(reserveId => this.getKyberSellQuotes(reserveId, makerToken, takerToken, takerFillAmounts), @@ -1106,6 +1148,16 @@ export class SamplerOperations { ); }); return sushiOps; + case ERC20BridgeSource.CryptoCom: + const cryptoComOps = [ + this.getCryptoComBuyQuotes([takerToken, makerToken], makerFillAmounts), + ]; + intermediateTokens.forEach(t => { + cryptoComOps.push( + this.getCryptoComBuyQuotes([takerToken, t, makerToken], makerFillAmounts), + ); + }); + return cryptoComOps; case ERC20BridgeSource.Kyber: return getKyberReserveIdsForPair(takerToken, makerToken).map(reserveId => this.getKyberBuyQuotes(reserveId, 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 536f8988ec..a851b8018e 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/types.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/types.ts @@ -49,6 +49,7 @@ export enum ERC20BridgeSource { SnowSwap = 'SnowSwap', SushiSwap = 'SushiSwap', Dodo = 'DODO', + CryptoCom = 'CryptoCom', } // tslint:disable: enum-naming diff --git a/packages/asset-swapper/test/market_operation_utils_test.ts b/packages/asset-swapper/test/market_operation_utils_test.ts index 8d7f13d8a8..b450b3d2f2 100644 --- a/packages/asset-swapper/test/market_operation_utils_test.ts +++ b/packages/asset-swapper/test/market_operation_utils_test.ts @@ -64,6 +64,7 @@ const DEFAULT_EXCLUDED = [ ERC20BridgeSource.Cream, ERC20BridgeSource.Dodo, ERC20BridgeSource.LiquidityProvider, + ERC20BridgeSource.CryptoCom, ]; const BUY_SOURCES = BUY_SOURCE_FILTER.sources; const SELL_SOURCES = SELL_SOURCE_FILTER.sources; @@ -312,6 +313,7 @@ describe('MarketOperationUtils tests', () => { [ERC20BridgeSource.Shell]: _.times(NUM_SAMPLES, () => 0), [ERC20BridgeSource.Cream]: _.times(NUM_SAMPLES, () => 0), [ERC20BridgeSource.Dodo]: _.times(NUM_SAMPLES, () => 0), + [ERC20BridgeSource.CryptoCom]: _.times(NUM_SAMPLES, () => 0), }; const DEFAULT_RATES: RatesBySource = { @@ -371,6 +373,7 @@ describe('MarketOperationUtils tests', () => { [ERC20BridgeSource.Shell]: { poolAddress: randomAddress() }, [ERC20BridgeSource.Cream]: { poolAddress: randomAddress() }, [ERC20BridgeSource.Dodo]: {}, + [ERC20BridgeSource.CryptoCom]: { tokenAddressPath: [] }, }; const DEFAULT_OPS = { diff --git a/packages/migrations/src/migration.ts b/packages/migrations/src/migration.ts index 58d568614d..039f79fa08 100644 --- a/packages/migrations/src/migration.ts +++ b/packages/migrations/src/migration.ts @@ -332,6 +332,7 @@ export async function runMigrationsAsync( dodoBridge: NULL_ADDRESS, dodoHelper: NULL_ADDRESS, snowSwapBridge: NULL_ADDRESS, + cryptoComBridge: NULL_ADDRESS, weth: etherToken.address, }, );