From c6919eb25a77f21d9ba74a02cfda1d0ad5d94f59 Mon Sep 17 00:00:00 2001 From: Jacob Evans Date: Wed, 17 Feb 2021 10:14:38 +1000 Subject: [PATCH] feat: Mirror Protocol tokens (#142) * feat: Mirror Protocol tokens * added .tap to builder --- packages/asset-swapper/CHANGELOG.json | 4 + packages/asset-swapper/src/constants.ts | 6 +- packages/asset-swapper/src/index.ts | 2 +- packages/asset-swapper/src/swap_quoter.ts | 3 +- packages/asset-swapper/src/types.ts | 24 +---- .../utils/market_operation_utils/constants.ts | 96 ++++++++----------- .../utils/token_adjacency_graph_builder.ts | 25 +++++ .../test/market_operation_utils_test.ts | 2 - 8 files changed, 72 insertions(+), 90 deletions(-) create mode 100644 packages/asset-swapper/src/utils/token_adjacency_graph_builder.ts diff --git a/packages/asset-swapper/CHANGELOG.json b/packages/asset-swapper/CHANGELOG.json index 786cc88ec3..b19f13e9c7 100644 --- a/packages/asset-swapper/CHANGELOG.json +++ b/packages/asset-swapper/CHANGELOG.json @@ -17,6 +17,10 @@ { "note": "Add deployed `CurveLiquidityProvider` addresses", "pr": 144 + }, + { + "note": "Support `Mirror Protocol` with hops to `UST`", + "pr": 142 } ] }, diff --git a/packages/asset-swapper/src/constants.ts b/packages/asset-swapper/src/constants.ts index b35b6f6368..869daa0f8c 100644 --- a/packages/asset-swapper/src/constants.ts +++ b/packages/asset-swapper/src/constants.ts @@ -83,11 +83,7 @@ export const DEFAULT_WARNING_LOGGER: LogFunction = (obj, msg) => const EMPTY_BYTES32 = '0x0000000000000000000000000000000000000000000000000000000000000000'; export const INVALID_SIGNATURE = { signatureType: SignatureType.Invalid, v: 1, r: EMPTY_BYTES32, s: EMPTY_BYTES32 }; -export { - BRIDGE_ADDRESSES_BY_CHAIN, - DEFAULT_FEE_SCHEDULE, - DEFAULT_GAS_SCHEDULE, -} from './utils/market_operation_utils/constants'; +export { DEFAULT_FEE_SCHEDULE, DEFAULT_GAS_SCHEDULE } from './utils/market_operation_utils/constants'; export const constants = { ETH_GAS_STATION_API_URL, diff --git a/packages/asset-swapper/src/index.ts b/packages/asset-swapper/src/index.ts index 013aea09c1..0aa21d14b5 100644 --- a/packages/asset-swapper/src/index.ts +++ b/packages/asset-swapper/src/index.ts @@ -107,7 +107,7 @@ export { } from './types'; export { affiliateFeeUtils } from './utils/affiliate_fee_utils'; export { - BRIDGE_ADDRESSES_BY_CHAIN, + DEFAULT_TOKEN_ADJACENCY_GRAPH, DEFAULT_GAS_SCHEDULE, SOURCE_FLAGS, } from './utils/market_operation_utils/constants'; diff --git a/packages/asset-swapper/src/swap_quoter.ts b/packages/asset-swapper/src/swap_quoter.ts index 5f34ba64f9..357b54b611 100644 --- a/packages/asset-swapper/src/swap_quoter.ts +++ b/packages/asset-swapper/src/swap_quoter.ts @@ -5,7 +5,7 @@ import { BlockParamLiteral, SupportedProvider, ZeroExProvider } from 'ethereum-t import * as _ from 'lodash'; import { artifacts } from './artifacts'; -import { BRIDGE_ADDRESSES_BY_CHAIN, constants, INVALID_SIGNATURE } from './constants'; +import { constants, INVALID_SIGNATURE } from './constants'; import { AssetSwapperContractAddresses, MarketBuySwapQuote, @@ -102,7 +102,6 @@ export class SwapQuoter { this._rfqtOptions = rfqt; this._contractAddresses = options.contractAddresses || { ...getContractAddressesForChainOrThrow(chainId), - ...BRIDGE_ADDRESSES_BY_CHAIN[chainId], }; this._protocolFeeUtils = ProtocolFeeUtils.getInstance( constants.PROTOCOL_FEE_UTILS_POLLING_INTERVAL_IN_MS, diff --git a/packages/asset-swapper/src/types.ts b/packages/asset-swapper/src/types.ts index 32f82ad791..66909160d0 100644 --- a/packages/asset-swapper/src/types.ts +++ b/packages/asset-swapper/src/types.ts @@ -259,7 +259,7 @@ export interface SwapQuoterRfqtOpts { infoLogger?: LogFunction; } -export type AssetSwapperContractAddresses = ContractAddresses & BridgeContractAddresses; +export type AssetSwapperContractAddresses = ContractAddresses; /** * chainId: The ethereum chain id. Defaults to 1 (mainnet). @@ -344,25 +344,3 @@ export interface SamplerCallResult { } export type Omit = Pick>; -/** - * The Contract addresses of the deployed Bridges - */ -export interface BridgeContractAddresses { - uniswapBridge: string; - uniswapV2Bridge: string; - eth2DaiBridge: string; - kyberBridge: string; - curveBridge: string; - multiBridge: string; - balancerBridge: string; - bancorBridge: string; - mStableBridge: string; - mooniswapBridge: string; - sushiswapBridge: string; - shellBridge: string; - dodoBridge: string; - 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 110263b6a0..efc5335faa 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/constants.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/constants.ts @@ -1,7 +1,6 @@ -import { ChainId } from '@0x/contract-addresses'; import { BigNumber } from '@0x/utils'; -import { BridgeContractAddresses } from '../../types'; +import { TokenAdjacencyGraphBuilder } from '../token_adjacency_graph_builder'; import { SourceFilters } from './source_filters'; import { @@ -19,6 +18,7 @@ import { MultiHopFillData, SnowSwapFillData, SushiSwapFillData, + TokenAdjacencyGraph, UniswapV2FillData, } from './types'; @@ -102,6 +102,23 @@ export const SOURCE_FLAGS: { [source in ERC20BridgeSource]: number } = Object.as ...Object.values(ERC20BridgeSource).map((source: ERC20BridgeSource, index) => ({ [source]: 1 << index })), ); +const MIRROR_WRAPPED_TOKENS = { + mAAPL: '0xd36932143f6ebdedd872d5fb0651f4b72fd15a84', + mSLV: '0x9d1555d8cb3c846bb4f7d5b1b1080872c3166676', + mIAU: '0x1d350417d9787e000cc1b95d70e9536dcd91f373', + mAMZN: '0x0cae9e4d663793c2a2a0b211c1cf4bbca2b9caa7', + mGOOGL: '0x4b70ccd1cf9905be1faed025eadbd3ab124efe9a', + mTSLA: '0x21ca39943e91d704678f5d00b6616650f066fd63', + mQQQ: '0x13b02c8de71680e71f0820c996e4be43c2f57d15', + mTWTR: '0xedb0414627e6f1e3f082de65cd4f9c693d78cca9', + mMSFT: '0x41bbedd7286daab5910a1f15d12cbda839852bd7', + mNFLX: '0xc8d674114bac90148d11d3c1d33c61835a0f9dcd', + mBABA: '0x676ce85f66adb8d7b8323aeefe17087a3b8cb363', + mUSO: '0x31c63146a635eb7465e5853020b39713ac356991', + mVIXY: '0xf72fcd9dcf0190923fadd44811e240ef4533fc86', + mLUNA: '0xd2877702675e6ceb975b4a1dff9fb7baf4c91ea9', +}; + // Mainnet tokens // Not an exhaustive list, just enough so we don't repeat ourselves export const TOKENS = { @@ -119,7 +136,6 @@ export const TOKENS = { mUSD: '0xe2f2a5c287993345a840db3b0845fbc70f5935a5', USDN: '0x674c6ad92fd080e4004b2312b45f796a192d27a0', dUSD: '0x5bc25f649fc4e26069ddf4cf4010f9f706c23831', - UST: '0xa47c8bf37f92abed4a126bda807a7b7498661acd', // Bitcoins WBTC: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', RenBTC: '0xeb4c2781e4eba804ce9a9803c67d0893436bb27d', @@ -138,6 +154,10 @@ export const TOKENS = { EURS: '0xdb25f211ab05b1c97d595516f45794528a807ad8', sEUR: '0xd71ecff9342a5ced620049e616c5035f1db98620', sETH: '0x5e74c9036fb86bd7ecdcb084a0673efc32ea31cb', + // Mirror Protocol + UST: '0xa47c8bf37f92abed4a126bda807a7b7498661acd', + MIR: '0x09a3ecafa817268f77be1283176b946c4ff2e608', + ...MIRROR_WRAPPED_TOKENS, }; export const POOLS = { @@ -169,6 +189,22 @@ export const POOLS = { curve_aave: '0xdebf20617708857ebe4f679508e7b7863a8a8eee', // 25.aave }; +export const DEFAULT_INTERMEDIATE_TOKENS = [TOKENS.WETH, TOKENS.USDT, TOKENS.DAI, TOKENS.USDC]; + +export const DEFAULT_TOKEN_ADJACENCY_GRAPH: TokenAdjacencyGraph = new TokenAdjacencyGraphBuilder({ + default: DEFAULT_INTERMEDIATE_TOKENS, +}) + // Mirror Protocol + .tap(builder => { + builder + .add(TOKENS.MIR, TOKENS.UST) + .add(TOKENS.UST, [TOKENS.MIR, ...Object.values(MIRROR_WRAPPED_TOKENS)]) + .add(TOKENS.USDT, TOKENS.UST); + Object.values(MIRROR_WRAPPED_TOKENS).forEach(t => builder.add(t, TOKENS.UST)); + }) + // Build + .build(); + /** * Mainnet Curve configuration * The tokens are in order of their index, which each curve defines @@ -456,60 +492,6 @@ export const BALANCER_SUBGRAPH_URL = 'https://api.thegraph.com/subgraphs/name/ba export const BALANCER_TOP_POOLS_FETCHED = 250; export const BALANCER_MAX_POOLS_FETCHED = 3; -const EMPTY_BRIDGE_ADDRESSES: BridgeContractAddresses = { - uniswapBridge: NULL_ADDRESS, - uniswapV2Bridge: NULL_ADDRESS, - eth2DaiBridge: NULL_ADDRESS, - kyberBridge: NULL_ADDRESS, - curveBridge: NULL_ADDRESS, - multiBridge: NULL_ADDRESS, - balancerBridge: NULL_ADDRESS, - bancorBridge: NULL_ADDRESS, - mStableBridge: NULL_ADDRESS, - mooniswapBridge: NULL_ADDRESS, - sushiswapBridge: NULL_ADDRESS, - shellBridge: NULL_ADDRESS, - dodoBridge: NULL_ADDRESS, - creamBridge: NULL_ADDRESS, - snowswapBridge: NULL_ADDRESS, - swerveBridge: NULL_ADDRESS, - cryptoComBridge: NULL_ADDRESS, -}; - -export const BRIDGE_ADDRESSES_BY_CHAIN: { [chainId in ChainId]: BridgeContractAddresses } = { - [ChainId.Mainnet]: { - uniswapBridge: '0x36691c4f426eb8f42f150ebde43069a31cb080ad', - uniswapV2Bridge: '0xdcd6011f4c6b80e470d9487f5871a0cba7c93f48', - kyberBridge: '0xadd97271402590564ddd8ad23cb5317b1fb0fffb', - eth2DaiBridge: '0x991c745401d5b5e469b8c3e2cb02c748f08754f1', - curveBridge: '0x1796cd592d19e3bcd744fbb025bb61a6d8cb2c09', - multiBridge: '0xc03117a8c9bde203f70aa911cb64a7a0df5ba1e1', - balancerBridge: '0xfe01821ca163844203220cd08e4f2b2fb43ae4e4', - bancorBridge: '0xc880c252db7c51f74161633338a3bdafa8e65276', - mStableBridge: '0x2bf04fcea05f0989a14d9afa37aa376baca6b2b3', - mooniswapBridge: '0x02b7eca484ad960fca3f7709e0b2ac81eec3069c', - sushiswapBridge: '0x47ed0262a0b688dcb836d254c6a2e96b6c48a9f5', - shellBridge: '0xf1c0811e3788caae7dbfae43da9d9131b1a8a148', - dodoBridge: '0xe9da66965a9344aab2167e6813c03f043cc7a6ca', - creamBridge: '0xb9d4bf2c8dab828f4ffb656acdb6c2b497d44f25', - swerveBridge: '0xf9786d5eb1de47fa56a8f7bb387653c6d410bfee', - snowswapBridge: '0xb1dbe83d15236ec10fdb214c6b89774b454754fd', - cryptoComBridge: '0x015850307f6aab4ac6631923ceefe71b57492c9b', - }, - [ChainId.Kovan]: { - ...EMPTY_BRIDGE_ADDRESSES, - uniswapBridge: '0x0e85f89f29998df65402391478e5924700c0079d', - uniswapV2Bridge: '0x7b3530a635d099de0534dc27e46cd7c57578c3c8', - eth2DaiBridge: '0x2d47147429b474d2e4f83e658015858a1312ed5b', - kyberBridge: '0xaecfa25920f892b6eb496e1f6e84037f59da7f44', - curveBridge: '0x81c0ab53a7352d2e97f682a37cba44e54647eefb', - balancerBridge: '0x407b4128e9ecad8769b2332312a9f655cb9f5f3a', - }, - [ChainId.Rinkeby]: EMPTY_BRIDGE_ADDRESSES, - [ChainId.Ropsten]: EMPTY_BRIDGE_ADDRESSES, - [ChainId.Ganache]: EMPTY_BRIDGE_ADDRESSES, -}; - /** * Calculated gross gas cost of the underlying exchange. * The cost of switching from one source to another, assuming diff --git a/packages/asset-swapper/src/utils/token_adjacency_graph_builder.ts b/packages/asset-swapper/src/utils/token_adjacency_graph_builder.ts new file mode 100644 index 0000000000..c08dfc0cf5 --- /dev/null +++ b/packages/asset-swapper/src/utils/token_adjacency_graph_builder.ts @@ -0,0 +1,25 @@ +import _ = require('lodash'); + +import { TokenAdjacencyGraph } from './market_operation_utils/types'; + +export class TokenAdjacencyGraphBuilder { + constructor(private readonly tokenAdjacency: TokenAdjacencyGraph) {} + + public add(from: string, to: string | string[]): TokenAdjacencyGraphBuilder { + if (!this.tokenAdjacency[from]) { + this.tokenAdjacency[from] = [...this.tokenAdjacency.default]; + } + this.tokenAdjacency[from] = [...(Array.isArray(to) ? to : [to]), ...this.tokenAdjacency[from]]; + this.tokenAdjacency[from] = _.uniqBy(this.tokenAdjacency[from], a => a.toLowerCase()); + return this; + } + + public tap(cb: (builder: TokenAdjacencyGraphBuilder) => void): TokenAdjacencyGraphBuilder { + cb(this); + return this; + } + + public build(): TokenAdjacencyGraph { + return this.tokenAdjacency; + } +} diff --git a/packages/asset-swapper/test/market_operation_utils_test.ts b/packages/asset-swapper/test/market_operation_utils_test.ts index 0f318e1b25..04b3a61a88 100644 --- a/packages/asset-swapper/test/market_operation_utils_test.ts +++ b/packages/asset-swapper/test/market_operation_utils_test.ts @@ -19,7 +19,6 @@ import { NativeOrderWithFillableAmounts } from '../src/types'; import { MarketOperationUtils } from '../src/utils/market_operation_utils/'; import { BalancerPoolsCache } from '../src/utils/market_operation_utils/balancer_utils'; import { - BRIDGE_ADDRESSES_BY_CHAIN, BUY_SOURCE_FILTER, POSITIVE_INF, SELL_SOURCE_FILTER, @@ -110,7 +109,6 @@ describe('MarketOperationUtils tests', () => { const CHAIN_ID = ChainId.Mainnet; const contractAddresses = { ...getContractAddressesForChainOrThrow(CHAIN_ID), - ...BRIDGE_ADDRESSES_BY_CHAIN[CHAIN_ID], }; function getMockedQuoteRequestor(