123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161 |
- // SPDX-License-Identifier: BUSL-1.1
- pragma solidity 0.7.6;
- pragma abicoder v2;
- import "../interfaces/IMirrorgateFeeLibrary.sol";
- import "../Pool.sol";
- import "../Factory.sol";
- import "@openzeppelin/contracts/math/SafeMath.sol";
- import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
- contract MirrorgateFeeLibraryV02 is IMirrorgateFeeLibrary, Ownable, ReentrancyGuard {
- using SafeMath for uint256;
- //---------------------------------------------------------------------------
- // VARIABLES
- // equilibrium func params. all in BPs * 10 ^ 2, i.e. 1 % = 10 ^ 6 units
- uint256 public constant DENOMINATOR = 1e18;
- uint256 public constant DELTA_1 = 6000 * 1e14;
- uint256 public constant DELTA_2 = 500 * 1e14;
- uint256 public constant LAMBDA_1 = 40 * 1e14;
- uint256 public constant LAMBDA_2 = 9960 * 1e14;
- uint256 public constant LP_FEE = 45 * 1e13;
- uint256 public constant PROTOCOL_FEE = 15 * 1e13;
- uint256 public constant PROTOCOL_SUBSIDY = 3 * 1e13;
- Factory public immutable factory;
- constructor(address _factory) {
- require(_factory != address(0x0), "FeeLibrary: Factory cannot be 0x0");
- factory = Factory(_factory);
- }
- function getFees(
- uint256 _srcPoolId,
- uint256 _dstPoolId,
- uint16 _dstChainId,
- address, /*_from*/
- uint256 _amountSD
- ) external view override returns (Pool.SwapObj memory s) {
- // calculate the protocol fee
- s.protocolFee = _amountSD.mul(PROTOCOL_FEE).div(DENOMINATOR);
- // calculate the equilibrium Fee
- Pool pool = factory.getPool(_srcPoolId);
- Pool.ChainPath memory chainPath = pool.getChainPath(_dstChainId, _dstPoolId);
- // calculate the equilibrium fee
- (uint256 eqFee, uint256 protocolSubsidy) = _getEquilibriumFee(chainPath.idealBalance, chainPath.balance, _amountSD);
- s.eqFee = eqFee;
- s.protocolFee = s.protocolFee.sub(protocolSubsidy);
- // calculate the equilibrium reward
- address tokenAddress = pool.token();
- uint256 currentAssetSD = IERC20(tokenAddress).balanceOf(address(pool)).div(pool.convertRate());
- uint256 lpAsset = pool.totalLiquidity();
- if (lpAsset > currentAssetSD) {
- // in deficit
- uint256 poolDeficit = lpAsset.sub(currentAssetSD);
- uint256 rewardPoolSize = pool.eqFeePool();
- // reward capped at rewardPoolSize
- uint256 eqRewards = rewardPoolSize.mul(_amountSD).div(poolDeficit);
- if (eqRewards > rewardPoolSize) {
- eqRewards = rewardPoolSize;
- }
- s.eqReward = eqRewards;
- }
- // calculate the LP fee.
- s.lpFee = _amountSD.mul(LP_FEE).div(DENOMINATOR);
- return s;
- }
- function getEquilibriumFee(
- uint256 idealBalance,
- uint256 beforeBalance,
- uint256 amountSD
- ) external pure returns (uint256, uint256) {
- return _getEquilibriumFee(idealBalance, beforeBalance, amountSD);
- }
- function getTrapezoidArea(
- uint256 lambda,
- uint256 yOffset,
- uint256 xUpperBound,
- uint256 xLowerBound,
- uint256 xStart,
- uint256 xEnd
- ) external pure returns (uint256) {
- return _getTrapezoidArea(lambda, yOffset, xUpperBound, xLowerBound, xStart, xEnd);
- }
- function _getEquilibriumFee(
- uint256 idealBalance,
- uint256 beforeBalance,
- uint256 amountSD
- ) internal pure returns (uint256, uint256) {
- require(beforeBalance >= amountSD, "Mirrorgate: not enough balance");
- uint256 afterBalance = beforeBalance.sub(amountSD);
- uint256 safeZoneMax = idealBalance.mul(DELTA_1).div(DENOMINATOR);
- uint256 safeZoneMin = idealBalance.mul(DELTA_2).div(DENOMINATOR);
- uint256 eqFee = 0;
- uint256 protocolSubsidy = 0;
- if (afterBalance >= safeZoneMax) {
- // no fee zone, protocol subsidize it.
- eqFee = amountSD.mul(PROTOCOL_SUBSIDY).div(DENOMINATOR);
- protocolSubsidy = eqFee;
- } else if (afterBalance >= safeZoneMin) {
- // safe zone
- uint256 proxyBeforeBalance = beforeBalance < safeZoneMax ? beforeBalance : safeZoneMax;
- eqFee = _getTrapezoidArea(LAMBDA_1, 0, safeZoneMax, safeZoneMin, proxyBeforeBalance, afterBalance);
- } else {
- // danger zone
- if (beforeBalance >= safeZoneMin) {
- // across 2 or 3 zones
- // part 1
- uint256 proxyBeforeBalance = beforeBalance < safeZoneMax ? beforeBalance : safeZoneMax;
- eqFee = eqFee.add(_getTrapezoidArea(LAMBDA_1, 0, safeZoneMax, safeZoneMin, proxyBeforeBalance, safeZoneMin));
- // part 2
- eqFee = eqFee.add(_getTrapezoidArea(LAMBDA_2, LAMBDA_1, safeZoneMin, 0, safeZoneMin, afterBalance));
- } else {
- // only in danger zone
- // part 2 only
- eqFee = eqFee.add(_getTrapezoidArea(LAMBDA_2, LAMBDA_1, safeZoneMin, 0, beforeBalance, afterBalance));
- }
- }
- return (eqFee, protocolSubsidy);
- }
- function _getTrapezoidArea(
- uint256 lambda,
- uint256 yOffset,
- uint256 xUpperBound,
- uint256 xLowerBound,
- uint256 xStart,
- uint256 xEnd
- ) internal pure returns (uint256) {
- require(xEnd >= xLowerBound && xStart <= xUpperBound, "Mirrorgate: balance out of bound");
- uint256 xBoundWidth = xUpperBound.sub(xLowerBound);
- // xStartDrift = xUpperBound.sub(xStart);
- uint256 yStart = xUpperBound.sub(xStart).mul(lambda).div(xBoundWidth).add(yOffset);
- // xEndDrift = xUpperBound.sub(xEnd)
- uint256 yEnd = xUpperBound.sub(xEnd).mul(lambda).div(xBoundWidth).add(yOffset);
- // compute the area
- uint256 deltaX = xStart.sub(xEnd);
- return yStart.add(yEnd).mul(deltaX).div(2).div(DENOMINATOR);
- }
- function getVersion() external pure override returns (string memory) {
- return "2.0.0";
- }
- }
|