-
Notifications
You must be signed in to change notification settings - Fork 83
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: adds task revenue-split-buy-back (#355)
adds hardhat task revenue-split-buy-back
- Loading branch information
1 parent
9fde20e
commit 2271bbe
Showing
3 changed files
with
260 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
/* eslint-disable no-await-in-loop */ | ||
import { Signer } from "@ethersproject/abstract-signer" | ||
import { ContractTransaction } from "ethers" | ||
import { BN, simpleToExactAmount } from "@utils/math" | ||
import { RevenueSplitBuyBack, ERC20__factory } from "types/generated" | ||
import { EncodedPaths, encodeUniswapPath, getWETHPath, quoteSwap } from "@utils/peripheral/uniswap" | ||
|
||
export interface MAssetSwap { | ||
address: string | ||
bAssetMinSlippage: number | ||
rewardMinSlippage: number | ||
mAssetMinBalance: number | BN | ||
} | ||
|
||
export interface MainParams { | ||
revenueSplitBuyBack: RevenueSplitBuyBack | ||
mAssets: MAssetSwap[] | ||
swapFees: number[] | ||
blockNumber: number | string | ||
} | ||
export interface BuyBackRewardsParams extends MainParams { | ||
minBassetsAmounts: BN[] | ||
minRewardsAmounts: BN[] | ||
uniswapPaths: EncodedPaths[] | ||
} | ||
|
||
/** | ||
* Calculate the minBassetsAmounts, minRewardsAmounts and uniswapPaths to execute on the buyback rewards. | ||
* | ||
* @param {Signer} signer | ||
* @param {MainParams} params | ||
* - mAssets: Addresses of mAssets that are to be sold for rewards. eg mUSD and mBTC. | ||
* - revenueSplitBuyBackAddress: The address of the revenue split buy back contract.; | ||
* @return {Promise<BuyBackRewardsParams>} | ||
* - minBassetsAmounts Minimum amount of bAsset tokens to receive for each redeem of mAssets. | ||
* The amount uses the decimal places of the bAsset. | ||
* Example 1: Redeeming 10,000 mUSD with a min 2% slippage to USDC which has 6 decimal places | ||
* minBassetsAmounts = 10,000 mAssets * slippage 0.98 * USDC decimals 1e6 = | ||
* 1e4 * 0.98 * 1e6 = 1e10 * 0.98 = 98e8 | ||
* | ||
* Example 2: Redeeming 1 mBTC with a min 5% slippage to WBTC which has 8 decimal places | ||
* minBassetsAmounts = 1 mAsset * slippage 0.95 * WBTC decimals 1e8 = | ||
* 0.95 * 1e8 = 95e6 | ||
* | ||
* - minRewardsAmounts Minimum amount of reward tokens received from the sale of bAssets. | ||
* The amount uses the decimal places of the rewards token. | ||
* Example 1: Swapping 10,000 USDC with a min 1% slippage to MTA which has 18 decimal places | ||
* minRewardsAmounts = 10,000 USDC * slippage 0.99 * MTA decimals 1e18 * MTA/USD rate 1.2 | ||
* = 1e4 * 0.99 * 1e18 * 1.2 = 1e22 * 0.99 = 99e20 | ||
* | ||
* Example 1: Swapping 1 WBTC with a min 3% slippage to MTA which has 18 decimal places | ||
* minRewardsAmounts = 1 WBTC * slippage 0.97 * MTA decimals 1e18 * MTA/BTC rate 0.00001 | ||
* = 1 * 0.97 * 1e18 * 0.00001 = 0.97 * 1e13 = 97e11 | ||
* | ||
* - uniswapPaths The Uniswap V3 bytes encoded paths. | ||
*/ | ||
export const calculateBuyBackRewardsQuote = async (signer: Signer, params: MainParams): Promise<BuyBackRewardsParams> => { | ||
const { revenueSplitBuyBack, mAssets, swapFees, blockNumber } = params | ||
const mAssetsToBuyBack: MAssetSwap[] = [] | ||
const minBassetsAmounts: BN[] = [] | ||
const minRewardsAmounts: BN[] = [] | ||
const uniswapPaths: EncodedPaths[] = [] | ||
|
||
const configScale: BN = await revenueSplitBuyBack.CONFIG_SCALE() | ||
const treasuryFee: BN = await revenueSplitBuyBack.treasuryFee() | ||
const rewardsToken = await revenueSplitBuyBack.REWARDS_TOKEN() | ||
|
||
const rewardsTokenContract = ERC20__factory.connect(rewardsToken, signer) | ||
const rTokenDecimals = await rewardsTokenContract.decimals() | ||
const rTokenSymbol = await rewardsTokenContract.symbol() | ||
|
||
for (let i = 0; i < mAssets.length; i = 1 + 1) { | ||
const mAsset = mAssets[i] | ||
const bAsset: string = await revenueSplitBuyBack.bassets(mAsset.address) | ||
const mAssetContract = ERC20__factory.connect(mAsset.address, signer) | ||
const bAssetContract = ERC20__factory.connect(bAsset, signer) | ||
|
||
const mAssetBalance: BN = await mAssetContract.balanceOf(revenueSplitBuyBack.address) | ||
const mAssetSymbol: string = await mAssetContract.symbol() | ||
|
||
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 | ||
const mAssetAmount = mAssetBalance.mul(configScale.sub(treasuryFee)).div(configScale) | ||
|
||
// calculate minBassetsAmounts | ||
const bAssetSlippage = 100 - mAsset.bAssetMinSlippage | ||
// mAssetAmount = 6000e18 * (98/100)/1e18 * 1e6 = 5880e6 (USDC) | ||
const minBassetsAmount = mAssetAmount | ||
.mul(bAssetSlippage) | ||
.div(100) | ||
.div(simpleToExactAmount(1)) | ||
.mul(simpleToExactAmount(1, bAssetDecimals)) | ||
minBassetsAmounts.push(minBassetsAmount) | ||
mAssetsToBuyBack.push(mAsset) | ||
|
||
// console for debugging purposes, do not delete | ||
|
||
console.table({ | ||
mAssetSymbol, | ||
mAssetBalance: mAssetBalance.toString(), | ||
configScale: configScale.toString(), | ||
treasuryFee: treasuryFee.toString(), | ||
bAssetSlippage: bAssetSlippage.toString(), | ||
bAssetDecimals: bAssetDecimals.toString(), | ||
mAssetAmount: mAssetAmount.toString(), | ||
minBassetsAmount: minBassetsAmount.toString(), | ||
}) | ||
|
||
// 2 ============ minRewardsAmount ============// | ||
|
||
const fromToken = { address: bAsset, decimals: bAssetDecimals } | ||
const toToken = { address: rewardsToken, decimals: rTokenDecimals } | ||
|
||
// eslint-disable-next-line no-await-in-loop | ||
const { outAmount, exchangeRate } = await quoteSwap( | ||
signer, | ||
fromToken, | ||
toToken, | ||
minBassetsAmount, | ||
blockNumber, | ||
undefined, | ||
swapFees, | ||
) | ||
const rewardSlippage = 100 - mAsset.rewardMinSlippage | ||
// minRewardsAmount = 5880e6 * (98/100) /1e6 * 1e18 = 5880e6 (USDC) | ||
// quote out amount of reward tokens with its decimals. | ||
const minRewardsAmount = outAmount.mul(rewardSlippage).div(100) | ||
|
||
minRewardsAmounts.push(minRewardsAmount) | ||
|
||
// console for debugging purposes, do not delete | ||
console.table({ | ||
bAssetSymbol, | ||
rTokenSymbol, | ||
rewardSlippage: rewardSlippage.toString(), | ||
mAssetAmount: mAssetAmount.toString(), | ||
minBassetsAmount: minBassetsAmount.toString(), | ||
minRewardsAmount: minRewardsAmount.toString(), | ||
outAmount: outAmount.toString(), | ||
exchangeRate: exchangeRate.toString(), | ||
bAssetDecimals: bAssetDecimals.toString(), | ||
rTokenDecimals: rTokenDecimals.toString(), | ||
}) | ||
|
||
// 3 ============ Uniswap path ============// | ||
const uniswapPath = encodeUniswapPath(getWETHPath(bAsset, rewardsToken), [3000, 3000]) | ||
uniswapPaths.push(uniswapPath) | ||
console.log(`ts: swap ${bAssetSymbol} to ${rTokenSymbol}, encodeUniswapPath: ${uniswapPath.encoded.toString()}`) | ||
} | ||
} | ||
return { ...params, mAssets: mAssetsToBuyBack, minBassetsAmounts, minRewardsAmounts, uniswapPaths } | ||
} | ||
/** | ||
* Execute the buyback rewards of different mAssets. | ||
* | ||
* @param {Signer} signer | ||
* @param {MainParams} params | ||
* @return {*} {Promise<ContractTransaction>} | ||
*/ | ||
export const splitBuyBackRewards = async (signer: Signer, params: MainParams): Promise<ContractTransaction> => { | ||
const buyBackRewardsParams = await calculateBuyBackRewardsQuote(signer, params) | ||
const { mAssets, minBassetsAmounts, minRewardsAmounts, uniswapPaths, revenueSplitBuyBack } = buyBackRewardsParams | ||
const mAssetAddress = mAssets.map((m) => m.address) | ||
const uniswapPathsEncoded = uniswapPaths.map((u) => u.encoded) | ||
console.log(`ts: buyBackRewards | ||
mAssetAddress ${mAssetAddress} | ||
minBassetsAmounts ${minBassetsAmounts.toString()} | ||
minRewardsAmounts ${minRewardsAmounts.toString()} | ||
uniswapPathsEncoded ${uniswapPathsEncoded} | ||
`) | ||
return revenueSplitBuyBack.buyBackRewards(mAssetAddress, minBassetsAmounts, minRewardsAmounts, uniswapPathsEncoded) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters