From 527e69448253afe0e648e43e6b39fce747dc6b15 Mon Sep 17 00:00:00 2001 From: doncesarts Date: Mon, 25 Jul 2022 11:26:47 +0100 Subject: [PATCH] feat: adds uniswap util bestQuoteSwap --- tasks/emissions.ts | 7 ++++- tasks/utils/emissions-split-buy-back.ts | 18 +++++------ test-utils/peripheral/uniswap.ts | 42 ++++++++++++++++++++++++- 3 files changed, 56 insertions(+), 11 deletions(-) diff --git a/tasks/emissions.ts b/tasks/emissions.ts index f88c3081..1ea25246 100644 --- a/tasks/emissions.ts +++ b/tasks/emissions.ts @@ -182,6 +182,11 @@ subtask("revenue-split-buy-back", "Buy back MTA from mUSD and mBTC gov fees") bAssetMinSlippage: 1, // 1% rewardMinSlippage: 1, // 1% mAssetMinBalance: simpleToExactAmount(1000), // 1k USD + // Provide two fees options to the swap, function splitBuyBackRewards will get the best quote. + swapFees: [ + [3000, 3000], + [500, 10000], + ], // [USDC/WETH 0.3%, MTA/WETH 0.3%] [USDC/WETH 0.05%, MTA/WETH 1%] } const mbtc = { @@ -189,12 +194,12 @@ subtask("revenue-split-buy-back", "Buy back MTA from mUSD and mBTC gov fees") bAssetMinSlippage: 3, // 3% rewardMinSlippage: 2, // 2% mAssetMinBalance: simpleToExactAmount(10, 14), // 10 wBTC + swapFees: [[3000, 3000]], // 0.3%, 0.3% } const mAssets = [musd, mbtc] const request = { mAssets, revenueSplitBuyBack, - swapFees: [3000, 3000], // 0.3%, 0.3%, pools fee blockNumber: "latest", } const tx = await splitBuyBackRewards(signer, request) diff --git a/tasks/utils/emissions-split-buy-back.ts b/tasks/utils/emissions-split-buy-back.ts index 9b1bdd47..2c32d6a1 100644 --- a/tasks/utils/emissions-split-buy-back.ts +++ b/tasks/utils/emissions-split-buy-back.ts @@ -3,19 +3,19 @@ import { Signer } from "@ethersproject/abstract-signer" import { ContractTransaction } from "ethers" import { BN, simpleToExactAmount } from "@utils/math" import { RevenueSplitBuyBack, IERC20Metadata__factory, Masset__factory } from "types/generated" -import { EncodedPaths, encodeUniswapPath, getWETHPath, quoteSwap } from "@utils/peripheral/uniswap" +import { EncodedPaths, encodeUniswapPath, getWETHPath, bestQuoteSwap } from "@utils/peripheral/uniswap" export interface MAssetSwap { address: string bAssetMinSlippage: number rewardMinSlippage: number mAssetMinBalance: number | BN + swapFees: number[][] // Options of fees to quote } export interface MainParams { revenueSplitBuyBack: RevenueSplitBuyBack mAssets: MAssetSwap[] - swapFees: number[] blockNumber: number | string } export interface BuyBackRewardsParams extends MainParams { @@ -56,7 +56,7 @@ export interface BuyBackRewardsParams extends MainParams { * - uniswapPaths The Uniswap V3 bytes encoded paths. */ export const calculateBuyBackRewardsQuote = async (signer: Signer, params: MainParams): Promise => { - const { revenueSplitBuyBack, mAssets, swapFees, blockNumber } = params + const { revenueSplitBuyBack, mAssets, blockNumber } = params const mAssetsToBuyBack: MAssetSwap[] = [] const minBassetsAmounts: BN[] = [] const minRewardsAmounts: BN[] = [] @@ -81,8 +81,6 @@ export const calculateBuyBackRewardsQuote = async (signer: Signer, params: MainP const bAssetDecimals = await bAssetContract.decimals() const bAssetSymbol: string = await bAssetContract.symbol() - // console.log(`ts: ${mAssetSymbol} balance: ${mAssetBalance.toString()}`); - // Validate if the mAsset balance is grater than the minimum balance to buy back, default is zero. if (mAssetBalance.gt(mAsset.mAssetMinBalance)) { // mAssetAmount = 10000e18 * (1e18 - 0.4e18 / 1e18) = 6000e18 @@ -113,6 +111,7 @@ export const calculateBuyBackRewardsQuote = async (signer: Signer, params: MainP mAssetAmount: mAssetAmount.toString(), minBassetsAmount: minBassetsAmount.toString(), bAssetRedeemAmount: bAssetRedeemAmount.toString(), + swapFees: mAsset.swapFees.toString(), }) // 2 ============ minRewardsAmount ============// @@ -120,15 +119,15 @@ export const calculateBuyBackRewardsQuote = async (signer: Signer, params: MainP const fromToken = { address: bAsset, decimals: bAssetDecimals } const toToken = { address: rewardsToken, decimals: rTokenDecimals } + // Get the best quote possible // eslint-disable-next-line no-await-in-loop - const { outAmount, exchangeRate } = await quoteSwap( + const { outAmount, exchangeRate, fees } = await bestQuoteSwap( signer, fromToken, toToken, bAssetRedeemAmount, blockNumber, - undefined, - swapFees, + mAsset.swapFees, ) const rewardSlippage = 100 - mAsset.rewardMinSlippage // minRewardsAmount = 5880e6 * (98/100) /1e6 * 1e18 = 5880e6 (USDC) @@ -149,10 +148,11 @@ export const calculateBuyBackRewardsQuote = async (signer: Signer, params: MainP exchangeRate: exchangeRate.toString(), bAssetDecimals: bAssetDecimals.toString(), rTokenDecimals: rTokenDecimals.toString(), + bestFees: fees.toString(), }) // 3 ============ Uniswap path ============// - const uniswapPath = encodeUniswapPath(getWETHPath(bAsset, rewardsToken), swapFees) + const uniswapPath = encodeUniswapPath(getWETHPath(bAsset, rewardsToken), fees) uniswapPaths.push(uniswapPath) console.log(`ts: swap ${bAssetSymbol} to ${rTokenSymbol}, encodeUniswapPath: ${uniswapPath.encoded.toString()}`) } diff --git a/test-utils/peripheral/uniswap.ts b/test-utils/peripheral/uniswap.ts index dfe2aec2..33eb9f9b 100644 --- a/test-utils/peripheral/uniswap.ts +++ b/test-utils/peripheral/uniswap.ts @@ -12,6 +12,7 @@ export interface EncodedPaths { export interface SwapQuote { outAmount: BN exchangeRate: BN + fees: number[] } export interface Token { address: string @@ -74,5 +75,44 @@ export const quoteSwap = async ( const outAmount = await quoteExactInput(quoter, encodedPath.encoded, inAmount, blockNumber) const exchangeRate = inAmount.div(outAmount.div(simpleToExactAmount(1, to.decimals))) // Exchange rate is not precise enough, better to relay on the output amount. - return { outAmount, exchangeRate } + return { outAmount, exchangeRate, fees } +} +/** + * For the same pair of tokens, it gives the best quote based on the router fees. + * If only one fee pair is provided it returns only that fee route quote. + * + * @param {Signer} signer + * @param {Token} from + * @param {Token} to + * @param {BN} inAmount + * @param {(number | string)} blockNumber + * @param {number[][]} fees + * @param {string[]} [path] + * @return {*} {Promise} + */ +export const bestQuoteSwap = async ( + signer: Signer, + from: Token, + to: Token, + inAmount: BN, + blockNumber: number | string, + fees: number[][], + path?: string[], +): Promise => { + // Get quote value from UniswapV3 + const uniswapPath = path || getWETHPath(from.address, to.address) + const quoter = IUniswapV3Quoter__factory.connect(uniswapQuoterV3Address, signer) + + // Use Uniswap V3 + // Exchange rate is not precise enough, better to relay on the output amount. + const quotes = await Promise.all( + fees.map(async (feePair) => { + const encodedPath = encodeUniswapPath(uniswapPath, feePair) + const outAmount = await quoteExactInput(quoter, encodedPath.encoded, inAmount, blockNumber) + const exchangeRate = inAmount.div(outAmount.div(simpleToExactAmount(1, to.decimals))) + return { encodedPath, outAmount, exchangeRate, fees: feePair } + }), + ) + // Get the quote that gives more output amount + return quotes.reduce((bestQuote, quote) => (bestQuote.outAmount > quote.outAmount ? bestQuote : quote)) }