feat: [asset-swapper] Add Crypto.com as a source (#43)

* feat: [asset-swapper] Add Crypto.com as a source

* Exclude in tests

* Disable hop sources to avoid excess inaccuracy

* Added CryptoCom Bridge and FQT rollup

* update test

* Deploy CryptoCom bridge

* Update package.json

* CHANGELOGs
This commit is contained in:
Jacob Evans 2020-12-01 12:52:48 +10:00 committed by GitHub
parent 0c08353b2c
commit f698721484
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 339 additions and 4 deletions

View File

@ -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;
}
}

View File

@ -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": {

View File

@ -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,

View File

@ -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';

View File

@ -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,

View File

@ -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';

View File

@ -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",

View File

@ -53,6 +53,10 @@
{
"note": "Add a permissionless transformer deployer",
"pr": 55
},
{
"note": "Add Crypto.com to `BridgeAdapter`",
"pr": 43
}
]
},

View File

@ -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,

View File

@ -26,6 +26,7 @@ contract MixinAdapterAddresses
address balancerBridge;
address creamBridge;
address curveBridge;
address cryptoComBridge;
address dodoBridge;
address kyberBridge;
address mooniswapBridge;

View File

@ -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];
}
}

View File

@ -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",

View File

@ -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,

View File

@ -81,6 +81,7 @@ blockchainTests.resets('FillQuoteTransformer', env => {
dodoBridge: NULL_ADDRESS,
dodoHelper: NULL_ADDRESS,
snowSwapBridge: NULL_ADDRESS,
cryptoComBridge: NULL_ADDRESS,
},
);
transformer = await FillQuoteTransformerContract.deployFrom0xArtifactAsync(

View File

@ -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';

View File

@ -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",

View File

@ -1,4 +1,13 @@
[
{
"version": "5.3.0",
"changes": [
{
"note": "Added Crypto.com",
"pr": 43
}
]
},
{
"version": "5.2.0",
"changes": [

View File

@ -426,4 +426,5 @@ export interface BridgeContractAddresses {
creamBridge: string;
swerveBridge: string;
snowswapBridge: string;
cryptoComBridge: string;
}

View File

@ -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<FeeSchedule> = {
},
[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.

View File

@ -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<SushiSwapFillData>).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<KyberFillData>).fillData!; // tslint:disable-line:no-non-null-assertion
makerAssetData = assetDataUtils.encodeERC20BridgeAssetData(

View File

@ -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<SushiSwapFillData> {
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<SushiSwapFillData> {
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),

View File

@ -49,6 +49,7 @@ export enum ERC20BridgeSource {
SnowSwap = 'SnowSwap',
SushiSwap = 'SushiSwap',
Dodo = 'DODO',
CryptoCom = 'CryptoCom',
}
// tslint:disable: enum-naming

View File

@ -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 = {

View File

@ -332,6 +332,7 @@ export async function runMigrationsAsync(
dodoBridge: NULL_ADDRESS,
dodoHelper: NULL_ADDRESS,
snowSwapBridge: NULL_ADDRESS,
cryptoComBridge: NULL_ADDRESS,
weth: etherToken.address,
},
);