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
24 changed files with 339 additions and 4 deletions

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