import { applyDecimals , BN , simpleToExactAmount } from "@utils/math" ; import { FeederPool , ICurve__factory , Masset } from "types/generated" ; import { CurveRegistryExchange__factory } from "types/generated/factories/CurveRegistryExchange__factory" ; import { MusdEth } from "types/generated/MusdEth" ; import { MusdLegacy } from "types/generated/MusdLegacy" ; import { getChainAddress } from "./networkAddressFactory" ; import { QuantityFormatter } from "./quantity-formatters" ; import { isMusdLegacy } from "./snap-utils" ; import { Chain , PDAI , PUSDC , PUSDT , Token } from "./tokens" ; export interface Balances { total : BN ; save : BN ; earn : BN ; } export interface BlockInfo { fromBlockNumber : number ; toBlockNumber : number ; startTime : Date ; endTime : Date ; } export interface SwapRate { inputToken : Token ; inputAmountRaw : BN ; inputDisplay : string ; outputToken : Token ; mOutputRaw : BN ; curveOutputRaw : BN ; curveInverseOutputRaw : BN ; } export const outputSwapRate = ( swap , quantityFormatter ) : void => { const { inputToken , outputToken , mOutputRaw , curveOutputRaw } = swap ; const inputScaled = applyDecimals ( swap . inputAmountRaw , inputToken . decimals ) ; const mOutputScaled = applyDecimals ( mOutputRaw , outputToken . decimals ) ; const mBasicPoints = mOutputScaled . sub ( inputScaled ) . mul ( 10000 ) . div ( inputScaled ) ; const curveOutputScaled = applyDecimals ( curveOutputRaw , outputToken . decimals ) ; const curvePercent = curveOutputScaled . sub ( inputScaled ) . mul ( 10000 ) . div ( inputScaled ) ; const diffOutputs = mOutputRaw . sub ( curveOutputRaw ) . mul ( 10000 ) . div ( mOutputRaw ) ; const curveInverseOutputScaled = applyDecimals ( swap . curveInverseOutputRaw , swap . inputToken . decimals ) ; const arbProfit = curveInverseOutputScaled . sub ( inputScaled ) ; if ( curveOutputRaw . gt ( 0 ) ) { console . log ( `${ swap . inputDisplay . padStart ( 9 ) } ${ inputToken . symbol . padEnd ( 6 ) } -> ${ outputToken . symbol . padEnd ( 6 ) } ${ quantityFormatter ( mOutputRaw , outputToken . decimals , 12 ) } ${ mBasicPoints . toString ( ) . padStart ( 4 ) }bps Curve ${ quantityFormatter ( curveOutputRaw , outputToken . decimals , 12 ) } ${ curvePercent . toString ( ) . padStart ( 4 ) }bps ${ diffOutputs . toString ( ) . padStart ( 3 ) }bps ${ quantityFormatter ( arbProfit , 18 , 8 ) }` ) ; } else { console . log ( `${ swap . inputDisplay . padStart ( 9 ) } ${ inputToken . symbol . padEnd ( 6 ) } -> ${ outputToken . symbol . padEnd ( 6 ) } ${ quantityFormatter ( mOutputRaw , outputToken . decimals , 12 ) } ${ mBasicPoints . toString ( ) . padStart ( 4 ) }bps` ) ; } } ; export const getSwapRates = async ( inputTokens , outputTokens , mAsset , toBlock , quantityFormatter , inputAmount = BN . from ( "1000" ) , chain = Chain . mainnet ) : Promise < SwapRate [ ] > => { if ( isMusdLegacy ( mAsset ) ) return [ ] ; const callOverride = { blockTag : toBlock , } ; const pairs = [ ] ; const mAssetSwapPromises = [ ] ; for ( const inputToken of inputTokens ) { for ( const outputToken of outputTokens ) { if ( inputToken . symbol !== outputToken . symbol ) { pairs . push ( { inputToken , outputToken , } ) ; const inputAmountRaw = simpleToExactAmount ( inputAmount , inputToken . decimals ) ; mAssetSwapPromises . push ( mAsset . getSwapOutput ( inputToken . address , outputToken . address , inputAmountRaw , callOverride ) ) ; } } } const mAssetSwaps = await Promise . all ( mAssetSwapPromises ) ; const curveSwapsPromises = [ ] ; pairs . forEach ( ( { inputToken , outputToken } , i ) => { if ( chain === Chain . mainnet ) { const curveRegistryExchangeAddress = getChainAddress ( "CurveRegistryExchange" , chain ) ; const curveRegistryExchange = CurveRegistryExchange__factory . connect ( curveRegistryExchangeAddress , mAsset . signer ) ; const curveSwapPromise = curveRegistryExchange . get_best_rate ( inputToken . address , outputToken . address , simpleToExactAmount ( inputAmount , inputToken . decimals ) , callOverride ) ; const curveInverseSwapPromise = curveRegistryExchange . get_best_rate ( outputToken . address , inputToken . address , mAssetSwaps [ i ] , callOverride ) ; curveSwapsPromises . push ( curveSwapPromise , curveInverseSwapPromise ) ; } else if ( chain === Chain . polygon ) { const curvePool = ICurve__factory . connect ( "0x445FE580eF8d70FF569aB36e80c647af338db351" , mAsset . signer ) ; const curveIndexMap = { [ PDAI . address ] : 0 , [ PUSDC . address ] : 1 , [ PUSDT . address ] : 2 , } ; const inputIndex = curveIndexMap [ inputToken . address ] ; const outputIndex = curveIndexMap [ outputToken . address ] ; const curveSwapPromise = curvePool . get_dy ( inputIndex , outputIndex , simpleToExactAmount ( inputAmount , inputToken . decimals ) , callOverride ) ; const curveInverseSwapPromise = curvePool . get_dy ( outputIndex , inputIndex , mAssetSwaps [ i ] , callOverride ) ; curveSwapsPromises . push ( curveSwapPromise , curveInverseSwapPromise ) ; } } ) ; const curveSwaps = await Promise . all ( curveSwapsPromises ) ; const swaps = pairs . map ( ( { inputToken , outputToken } , i ) => ( { inputToken , inputAmountRaw : simpleToExactAmount ( inputAmount , inputToken . decimals ) , inputDisplay : inputAmount . toString ( ) , outputToken , mOutputRaw : mAssetSwaps [ i ] , curveOutputRaw : chain === Chain . mainnet ? curveSwaps [ i * 2 ] [ 1 ] : curveSwaps [ i * 2 ] , curveInverseOutputRaw : chain === Chain . mainnet ? curveSwaps [ i * 2 + 1 ] [ 1 ] : curveSwaps [ i * 2 + 1 ] , } ) ) ; swaps . forEach ( ( swap ) => { outputSwapRate ( swap , quantityFormatter ) ; } ) ; return swaps ; } ;