platypus integration

This commit is contained in:
Noah Khamliche
2022-05-09 23:15:43 -04:00
parent feede28293
commit 4c8629840b
14 changed files with 261 additions and 5 deletions

View File

@@ -37,6 +37,7 @@ import "./MultiBridgeSampler.sol";
import "./MStableSampler.sol";
import "./MooniswapSampler.sol";
import "./NativeOrderSampler.sol";
import "./PlatypusSampler.sol";
import "./ShellSampler.sol";
import "./SmoothySampler.sol";
import "./TwoHopSampler.sol";
@@ -64,6 +65,7 @@ contract ERC20BridgeSampler is
MooniswapSampler,
MultiBridgeSampler,
NativeOrderSampler,
PlatypusSampler,
ShellSampler,
SmoothySampler,
TwoHopSampler,

View File

@@ -0,0 +1,89 @@
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import "./interfaces/IPlatypus.sol";
import "./ApproximateBuys.sol";
import "./SamplerUtils.sol";
contract PlatypusSampler is
SamplerUtils,
ApproximateBuys
{
function sampleSellsFromPlatypus(
address pool,
address[] memory path,
uint256[] memory takerTokenAmounts
)
public
view
returns (uint256[] memory makerTokenAmounts)
{
uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples);
for (uint256 i = 0; i < numSamples; i++) {
try
IPlatypus(pool).quotePotentialSwap(path[0], path[1], takerTokenAmounts[i])
returns (uint256 amountAfterFees, uint256 feeAmount)
{
makerTokenAmounts[i] = amountAfterFees;
// Break early if there are 0 amounts
if (makerTokenAmounts[i] == 0) {
break;
}
} catch (bytes memory) {
// Swallow failures, leaving all results as zero.
break;
}
}
}
function sampleBuysFromPlatypus(
address pool,
address[] memory path,
uint256[] memory makerTokenAmounts
)
public
view
returns (uint256[] memory takerTokenAmounts)
{
address[] memory invertBuyPath = new address[](2);
invertBuyPath[0] = path[1];
invertBuyPath[1] = path[0];
return _sampleApproximateBuys(
ApproximateBuyQuoteOpts({
makerTokenData: abi.encode(pool, invertBuyPath),
takerTokenData: abi.encode(pool, path),
getSellQuoteCallback: _sampleSellForApproximateBuyFromPlatypus
}),
makerTokenAmounts
);
}
function _sampleSellForApproximateBuyFromPlatypus(
bytes memory makerTokenData,
bytes memory takerTokenData,
uint256 sellAmount
)
private
view
returns (uint256 buyAmount)
{
(address _pool, address[] memory _path ) = abi.decode(takerTokenData, (address, address[]));
(bool success, bytes memory resultData) = address(this).staticcall(abi.encodeWithSelector(
this.sampleSellsFromPlatypus.selector,
_pool,
_path,
_toSingleValueArray(sellAmount)
));
if(!success) {
return 0;
}
// solhint-disable-next-line indent
return abi.decode(resultData, (uint256[]))[0];
}
}

View File

@@ -0,0 +1,12 @@
pragma solidity ^0.6;
interface IPlatypus {
function quotePotentialSwap(
address fromToken,
address toToken,
uint256 fromAmount
) external view returns (uint256 potentialOutcome, uint256 haircut);
function assetOf(address token) external view returns (address);
}

View File

@@ -39,7 +39,7 @@
"config": {
"publicInterfaceContracts": "ERC20BridgeSampler,BalanceChecker,FakeTaker",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
"abis": "./test/generated-artifacts/@(ApproximateBuys|BalanceChecker|BalancerSampler|BalancerV2Sampler|BancorSampler|CompoundSampler|CurveSampler|DODOSampler|DODOV2Sampler|DummyLiquidityProvider|ERC20BridgeSampler|FakeTaker|GMXSampler|IBalancer|IBancor|ICurve|IGMX|IKyberNetwork|IMStable|IMooniswap|IMultiBridge|IShell|ISmoothy|IUniswapExchangeQuotes|IUniswapV2Router01|KyberDmmSampler|KyberSampler|LidoSampler|LiquidityProviderSampler|MStableSampler|MakerPSMSampler|MooniswapSampler|MultiBridgeSampler|NativeOrderSampler|SamplerUtils|ShellSampler|SmoothySampler|TestERC20BridgeSampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler|UniswapV3Sampler|UtilitySampler).json",
"abis": "./test/generated-artifacts/@(ApproximateBuys|BalanceChecker|BalancerSampler|BalancerV2Sampler|BancorSampler|CompoundSampler|CurveSampler|DODOSampler|DODOV2Sampler|DummyLiquidityProvider|ERC20BridgeSampler|FakeTaker|GMXSampler|IBalancer|IBancor|ICurve|IGMX|IKyberNetwork|IMStable|IMooniswap|IMultiBridge|IPlatypus|IShell|ISmoothy|IUniswapExchangeQuotes|IUniswapV2Router01|KyberDmmSampler|KyberSampler|LidoSampler|LiquidityProviderSampler|MStableSampler|MakerPSMSampler|MooniswapSampler|MultiBridgeSampler|NativeOrderSampler|PlatypusSampler|SamplerUtils|ShellSampler|SmoothySampler|TestERC20BridgeSampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler|UniswapV3Sampler|UtilitySampler).json",
"postpublish": {
"assets": []
}

View File

@@ -357,7 +357,7 @@ export class SwapQuoter {
if (nativeOrders.length === 0) {
nativeOrders.push(createDummyOrder(makerToken, takerToken));
}
// ** Prepare options for fetching market side liquidity **
// Scale fees by gas price.
const cloneOpts = _.omit(opts, 'gasPrice') as GetMarketOrdersOpts;

View File

@@ -62,8 +62,9 @@ import {
UNISWAPV2_ROUTER_BY_CHAIN_ID,
WAULTSWAP_ROUTER_BY_CHAIN_ID,
XSIGMA_MAINNET_INFOS,
PLATYPUS_AVALANCHE_INFOS,
} from './constants';
import { CurveInfo, ERC20BridgeSource } from './types';
import { CurveInfo, ERC20BridgeSource, PlatypusInfo } from './types';
/**
* Filter Kyber reserves which should not be used (0xbb bridged reserves)
@@ -463,6 +464,18 @@ export function getMobiusMoneyInfoForPair(chainId: ChainId, takerToken: string,
);
}
export function getPlatypusInfoForPair(chainId: ChainId, takerToken: string, makerToken: string): PlatypusInfo[] {
if (chainId !== ChainId.Avalanche) {
return [];
}
return Object.values(PLATYPUS_AVALANCHE_INFOS).filter(c =>
[makerToken, takerToken].every(
t =>
c.tokens.includes(t),
),
);
}
export function getShellLikeInfosForPair(
chainId: ChainId,
takerToken: string,

View File

@@ -27,6 +27,8 @@ import {
LiquidityProviderRegistry,
MakerPsmFillData,
MultiHopFillData,
PlatypusFillData,
PlatypusInfo,
PsmInfo,
TokenAdjacencyGraph,
UniswapV2FillData,
@@ -185,6 +187,7 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.AaveV2,
ERC20BridgeSource.Synapse,
ERC20BridgeSource.GMX,
ERC20BridgeSource.Platypus,
]),
[ChainId.Fantom]: new SourceFilters([
ERC20BridgeSource.MultiHop,
@@ -333,6 +336,7 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.AaveV2,
ERC20BridgeSource.Synapse,
ERC20BridgeSource.GMX,
ERC20BridgeSource.Platypus,
]),
[ChainId.Fantom]: new SourceFilters([
ERC20BridgeSource.MultiHop,
@@ -553,6 +557,7 @@ export const AVALANCHE_TOKENS = {
// native USDC on Avalanche
nUSDC: '0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e',
USDT: '0xc7198437980c041c805a1edcba50c1ce5db95118',
USDt: '0x9702230a8ea53601f5cd2dc00fdbc13d4df4a8c7',
aDAI: '0x47afa96cdc9fab46904a55a6ad4bf6660b53c38a',
aUSDC: '0x46a51127c3ce23fb7ab1de06226147f446e4a857',
aUSDT: '0x532e6537fea298397212f09a61e03311686f548e',
@@ -560,6 +565,10 @@ export const AVALANCHE_TOKENS = {
nUSD: '0xcfc37a6ab183dd4aed08c204d1c2773c0b1bdf46',
aWETH: '0x53f7c5869a859f0aec3d334ee8b4cf01e3492f21',
MIM: '0x130966628846bfd36ff31a822705796e8cb8c18d',
sAVAX: '0x2b2c81e08f1af8835a78bb2a90ae924ace0ea4be',
UST: '0xb599c3590f42f8f995ecfa0f85d2980b76862fc1',
FRAX: '0xd24c2ad096400b6fbcd2ad8b24e7acbc21a1da64',
};
export const CELO_TOKENS = {
@@ -835,6 +844,14 @@ export const ACRYPTOS_POOLS = {
acs3btc: '0xbe7caa236544d1b9a0e7f91e94b9f5bfd3b5ca81',
};
export const PLATYPUS_AVALANCHE_POOLS = {
usd: '0x66357dcace80431aee0a7507e2e361b7e2402370',
ust: '0xe0d166de15665bc4b7185b2e35e847e51316e126',
frax: '0xb8e567fc23c39c94a1f6359509d7b43d1fbed824',
mim: '0x30c30d826be87cd0a4b90855c2f38f7fcfe4eaa7',
sAVAX: '0x4658ea7e9960d6158a261104aaa160cc953bb6ba',
};
export const DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID = valueByChainId<string[]>(
{
[ChainId.Mainnet]: [
@@ -1818,6 +1835,34 @@ export const ACRYPTOS_BSC_INFOS: { [name: string]: CurveInfo } = {
}),
};
export const PLATYPUS_AVALANCHE_INFOS: { [name: string]: PlatypusInfo } = {
[PLATYPUS_AVALANCHE_POOLS.usd]: {
poolAddress: PLATYPUS_AVALANCHE_POOLS.usd,
tokens: [AVALANCHE_TOKENS.USDT, AVALANCHE_TOKENS.USDC, AVALANCHE_TOKENS.DAI, AVALANCHE_TOKENS.nUSDC, AVALANCHE_TOKENS.USDt],
gasSchedule: 350e3,
},
[PLATYPUS_AVALANCHE_POOLS.ust]: {
poolAddress: PLATYPUS_AVALANCHE_POOLS.ust,
tokens: [AVALANCHE_TOKENS.nUSDC, AVALANCHE_TOKENS.UST],
gasSchedule: 350e3,
},
[PLATYPUS_AVALANCHE_POOLS.frax]: {
poolAddress: PLATYPUS_AVALANCHE_POOLS.frax,
tokens: [AVALANCHE_TOKENS.nUSDC, AVALANCHE_TOKENS.FRAX], //liq issues
gasSchedule: 350e3,
},
[PLATYPUS_AVALANCHE_POOLS.mim]: {
poolAddress: PLATYPUS_AVALANCHE_POOLS.mim,
tokens: [AVALANCHE_TOKENS.MIM, AVALANCHE_TOKENS.nUSDC],
gasSchedule: 350e3,
},
[PLATYPUS_AVALANCHE_POOLS.sAVAX]: {
poolAddress: PLATYPUS_AVALANCHE_POOLS.sAVAX,
tokens: [AVALANCHE_TOKENS.WAVAX, AVALANCHE_TOKENS.sAVAX], //add unwrapping
gasSchedule: 350e3,
},
};
/**
* Kyber reserve prefixes
* 0xff Fed price reserve
@@ -2367,6 +2412,13 @@ export const GMX_VAULT_BY_CHAIN_ID = valueByChainId<string>(
NULL_ADDRESS,
);
export const PLATYPUS_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
{
[ChainId.Avalanche]: '0x73256ec7575d999c360c1eec118ecbefd8da7d12',
},
NULL_ADDRESS,
);
export const VIP_ERC20_BRIDGE_SOURCES_BY_CHAIN_ID = valueByChainId<ERC20BridgeSource[]>(
{
[ChainId.Mainnet]: [
@@ -2571,6 +2623,7 @@ export const DEFAULT_GAS_SCHEDULE: Required<FeeSchedule> = {
[ERC20BridgeSource.Pangolin]: uniswapV2CloneGasSchedule,
[ERC20BridgeSource.TraderJoe]: uniswapV2CloneGasSchedule,
[ERC20BridgeSource.GMX]: () => 450e3,
[ERC20BridgeSource.Platypus]: () => 450e3,
//
// Celo

View File

@@ -35,6 +35,7 @@ import {
OptimizedMarketOrder,
OptimizedMarketOrderBase,
OrderDomain,
PlatypusFillData,
ShellFillData,
UniswapV2FillData,
UniswapV3FillData,
@@ -211,6 +212,8 @@ export function getErc20BridgeSourceToBridgeSource(source: ERC20BridgeSource): s
return encodeBridgeSourceId(BridgeProtocol.Nerve, 'MobiusMoney');
case ERC20BridgeSource.GMX:
return encodeBridgeSourceId(BridgeProtocol.GMX, 'GMX');
case ERC20BridgeSource.Platypus:
return encodeBridgeSourceId(BridgeProtocol.Platypus, 'Platypus');
default:
throw new Error(AggregationError.NoBridgeForSource);
}
@@ -379,6 +382,14 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
gmxFillData.tokenAddressPath,
]);
break;
case ERC20BridgeSource.Platypus:
const platypusFillData = (order as OptimizedMarketBridgeOrder<PlatypusFillData>).fillData;
bridgeData = encoder.encode([
platypusFillData.router,
platypusFillData.pool,
platypusFillData.tokenAddressPath,
]);
break;
default:
throw new Error(AggregationError.NoBridgeForSource);
@@ -465,6 +476,7 @@ const balancerV2Encoder = AbiEncoder.create([
]);
const routerAddressPathEncoder = AbiEncoder.create('(address,address[])');
const gmxAddressPathEncoder = AbiEncoder.create('(address,address,address,address[])');
const platypusAddressPathEncoder = AbiEncoder.create('(address,address[],address[])');
const tokenAddressEncoder = AbiEncoder.create([{ name: 'tokenAddress', type: 'address' }]);
export const BRIDGE_ENCODERS: {
@@ -520,6 +532,7 @@ export const BRIDGE_ENCODERS: {
[ERC20BridgeSource.MorpheusSwap]: routerAddressPathEncoder,
// Avalanche
[ERC20BridgeSource.GMX]: gmxAddressPathEncoder,
[ERC20BridgeSource.Platypus]: platypusAddressPathEncoder,
// Celo
[ERC20BridgeSource.UbeSwap]: routerAddressPathEncoder,
// BSC

View File

@@ -14,6 +14,7 @@ import {
getCurveLikeInfosForPair,
getDodoV2Offsets,
getKyberOffsets,
getPlatypusInfoForPair,
getShellLikeInfosForPair,
isAllowedKyberReserveId,
isBadTokenForSource,
@@ -45,6 +46,7 @@ import {
NATIVE_FEE_TOKEN_BY_CHAIN_ID,
NULL_ADDRESS,
NULL_BYTES,
PLATYPUS_ROUTER_BY_CHAIN_ID,
SELL_SOURCE_FILTER_BY_CHAIN_ID,
UNISWAPV1_ROUTER_BY_CHAIN_ID,
UNISWAPV3_CONFIG_BY_CHAIN_ID,
@@ -86,6 +88,7 @@ import {
MakerPsmFillData,
MooniswapFillData,
MultiHopFillData,
PlatypusFillData,
PsmInfo,
ShellFillData,
SourceQuoteOperation,
@@ -1248,6 +1251,35 @@ export class SamplerOperations {
});
}
public getPlatypusSellQuotes(
router: string,
pool: string[],
tokenAddressPath: string[],
takerFillAmounts: BigNumber[],
): SourceQuoteOperation<PlatypusFillData> {
return new SamplerContractOperation({
source: ERC20BridgeSource.Platypus,
fillData: { router, pool, tokenAddressPath },
contract: this._samplerContract,
function: this._samplerContract.sampleSellsFromPlatypus,
params: [pool[0], tokenAddressPath, takerFillAmounts],
});
}
public getPlatypusBuyQuotes(
router: string,
pool: string[],
tokenAddressPath: string[],
makerFillAmounts: BigNumber[],
): SourceQuoteOperation<PlatypusFillData> {
return new SamplerContractOperation({
source: ERC20BridgeSource.Platypus,
fillData: { router, pool, tokenAddressPath },
contract: this._samplerContract,
function: this._samplerContract.sampleBuysFromPlatypus,
params: [pool[0], tokenAddressPath, makerFillAmounts],
});
}
public getMedianSellRate(
sources: ERC20BridgeSource[],
makerToken: string,
@@ -1648,8 +1680,19 @@ export class SamplerOperations {
takerFillAmounts,
);
}
default:
throw new Error(`Unsupported sell sample source: ${source}`);
case ERC20BridgeSource.Platypus: {
return getPlatypusInfoForPair(this.chainId, takerToken, makerToken).map(pool =>
this.getPlatypusSellQuotes(
PLATYPUS_ROUTER_BY_CHAIN_ID[this.chainId],
[pool.poolAddress],
[takerToken, makerToken],
takerFillAmounts,
),
);
}
default:
throw new Error(`Unsupported sell sample source: ${source}`);
}
}),
);
@@ -1961,6 +2004,16 @@ export class SamplerOperations {
makerFillAmounts,
);
}
case ERC20BridgeSource.Platypus: {
return getPlatypusInfoForPair(this.chainId, takerToken, makerToken).map(pool =>
this.getPlatypusBuyQuotes(
PLATYPUS_ROUTER_BY_CHAIN_ID[this.chainId],
[pool.poolAddress],
[takerToken, makerToken],
makerFillAmounts,
),
);
}
default:
throw new Error(`Unsupported buy sample source: ${source}`);
}

View File

@@ -96,6 +96,7 @@ export enum ERC20BridgeSource {
TraderJoe = 'TraderJoe',
// tslint:disable-next-line: enum-naming
GMX = 'GMX',
Platypus = 'Platypus',
// Celo only
UbeSwap = 'UbeSwap',
MobiusMoney = 'MobiusMoney',
@@ -190,6 +191,11 @@ export interface GeistInfo {
underlyingToken: string;
}
export interface PlatypusInfo {
poolAddress: string;
tokens: string[];
gasSchedule: number;
}
// Internal `fillData` field for `Fill` objects.
export interface FillData {}
@@ -232,6 +238,12 @@ export interface GMXFillData extends FillData {
tokenAddressPath: string[];
}
export interface PlatypusFillData extends FillData {
router: string;
pool: string[];
tokenAddressPath: string[];
}
export interface ShellFillData extends FillData {
poolAddress: string;
}

View File

@@ -26,6 +26,7 @@ import * as IKyberNetwork from '../test/generated-artifacts/IKyberNetwork.json';
import * as IMooniswap from '../test/generated-artifacts/IMooniswap.json';
import * as IMStable from '../test/generated-artifacts/IMStable.json';
import * as IMultiBridge from '../test/generated-artifacts/IMultiBridge.json';
import * as IPlatypus from '../test/generated-artifacts/IPlatypus.json';
import * as IShell from '../test/generated-artifacts/IShell.json';
import * as ISmoothy from '../test/generated-artifacts/ISmoothy.json';
import * as IUniswapExchangeQuotes from '../test/generated-artifacts/IUniswapExchangeQuotes.json';
@@ -39,6 +40,7 @@ import * as MooniswapSampler from '../test/generated-artifacts/MooniswapSampler.
import * as MStableSampler from '../test/generated-artifacts/MStableSampler.json';
import * as MultiBridgeSampler from '../test/generated-artifacts/MultiBridgeSampler.json';
import * as NativeOrderSampler from '../test/generated-artifacts/NativeOrderSampler.json';
import * as PlatypusSampler from '../test/generated-artifacts/PlatypusSampler.json';
import * as SamplerUtils from '../test/generated-artifacts/SamplerUtils.json';
import * as ShellSampler from '../test/generated-artifacts/ShellSampler.json';
import * as SmoothySampler from '../test/generated-artifacts/SmoothySampler.json';
@@ -71,6 +73,7 @@ export const artifacts = {
MooniswapSampler: MooniswapSampler as ContractArtifact,
MultiBridgeSampler: MultiBridgeSampler as ContractArtifact,
NativeOrderSampler: NativeOrderSampler as ContractArtifact,
PlatypusSampler: PlatypusSampler as ContractArtifact,
SamplerUtils: SamplerUtils as ContractArtifact,
ShellSampler: ShellSampler as ContractArtifact,
SmoothySampler: SmoothySampler as ContractArtifact,
@@ -87,6 +90,7 @@ export const artifacts = {
IMStable: IMStable as ContractArtifact,
IMooniswap: IMooniswap as ContractArtifact,
IMultiBridge: IMultiBridge as ContractArtifact,
IPlatypus: IPlatypus as ContractArtifact,
IShell: IShell as ContractArtifact,
ISmoothy: ISmoothy as ContractArtifact,
IUniswapExchangeQuotes: IUniswapExchangeQuotes as ContractArtifact,

View File

@@ -23,6 +23,7 @@ export * from '../test/generated-wrappers/i_kyber_network';
export * from '../test/generated-wrappers/i_m_stable';
export * from '../test/generated-wrappers/i_mooniswap';
export * from '../test/generated-wrappers/i_multi_bridge';
export * from '../test/generated-wrappers/i_platypus';
export * from '../test/generated-wrappers/i_shell';
export * from '../test/generated-wrappers/i_smoothy';
export * from '../test/generated-wrappers/i_uniswap_exchange_quotes';
@@ -37,6 +38,7 @@ export * from '../test/generated-wrappers/maker_p_s_m_sampler';
export * from '../test/generated-wrappers/mooniswap_sampler';
export * from '../test/generated-wrappers/multi_bridge_sampler';
export * from '../test/generated-wrappers/native_order_sampler';
export * from '../test/generated-wrappers/platypus_sampler';
export * from '../test/generated-wrappers/sampler_utils';
export * from '../test/generated-wrappers/shell_sampler';
export * from '../test/generated-wrappers/smoothy_sampler';

View File

@@ -27,6 +27,7 @@
"test/generated-artifacts/IMStable.json",
"test/generated-artifacts/IMooniswap.json",
"test/generated-artifacts/IMultiBridge.json",
"test/generated-artifacts/IPlatypus.json",
"test/generated-artifacts/IShell.json",
"test/generated-artifacts/ISmoothy.json",
"test/generated-artifacts/IUniswapExchangeQuotes.json",
@@ -40,6 +41,7 @@
"test/generated-artifacts/MooniswapSampler.json",
"test/generated-artifacts/MultiBridgeSampler.json",
"test/generated-artifacts/NativeOrderSampler.json",
"test/generated-artifacts/PlatypusSampler.json",
"test/generated-artifacts/SamplerUtils.json",
"test/generated-artifacts/ShellSampler.json",
"test/generated-artifacts/SmoothySampler.json",

View File

@@ -135,6 +135,7 @@ export enum BridgeProtocol {
AaveV2,
Compound,
GMX,
Platypus,
}
// tslint:enable: enum-naming