123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644 |
- // SPDX-License-Identifier: BUSL-1.1
- pragma solidity 0.7.6;
- pragma abicoder v2;
- // imports
- import "@openzeppelin/contracts/access/Ownable.sol";
- import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
- import "./LPTokenERC20.sol";
- import "./interfaces/IMirrorgateFeeLibrary.sol";
- // libraries
- import "@openzeppelin/contracts/math/SafeMath.sol";
- /// Pool contracts on other chains and managed by the Stargate protocol.
- contract Pool is LPTokenERC20, ReentrancyGuard {
- using SafeMath for uint256;
- //---------------------------------------------------------------------------
- // CONSTANTS
- bytes4 private constant SELECTOR = bytes4(keccak256(bytes("transfer(address,uint256)")));
- uint256 public constant BP_DENOMINATOR = 10000;
- //---------------------------------------------------------------------------
- // STRUCTS
- struct ChainPath {
- bool ready; // indicate if the counter chainPath has been created.
- uint16 dstChainId;
- uint256 dstPoolId;
- uint256 weight;
- uint256 balance;
- uint256 lkb;
- uint256 credits;
- uint256 idealBalance;
- }
- struct SwapObj {
- uint256 amount;
- uint256 eqFee;
- uint256 eqReward;
- uint256 lpFee;
- uint256 protocolFee;
- uint256 lkbRemove;
- }
- struct CreditObj {
- uint256 credits;
- uint256 idealBalance;
- }
- //---------------------------------------------------------------------------
- // VARIABLES
- // chainPath
- ChainPath[] public chainPaths; // list of connected chains with shared pools
- mapping(uint16 => mapping(uint256 => uint256)) public chainPathIndexLookup; // lookup for chainPath by chainId => poolId =>index
- // metadata
- uint256 public immutable poolId; // shared id between chains to represent same pool
- uint256 public sharedDecimals; // the shared decimals (lowest common decimals between chains)
- uint256 public localDecimals; // the decimals for the token
- uint256 public immutable convertRate; // the decimals for the token
- address public immutable token; // the token for the pool
- address public immutable router; // the token for the pool
- bool public stopSwap; // flag to stop swapping in extreme cases
- // Fee and Liquidity
- uint256 public totalLiquidity; // the total amount of tokens added on this side of the chain (fees + deposits - withdrawals)
- uint256 public totalWeight; // total weight for pool percentages
- uint256 public mintFeeBP; // fee basis points for the mint/deposit
- uint256 public protocolFeeBalance; // fee balance created from dao fee
- uint256 public mintFeeBalance; // fee balance created from mint fee
- uint256 public eqFeePool; // pool rewards in Shared Decimal format. indicate the total budget for reverse swap incentive
- address public feeLibrary; // address for retrieving fee params for swaps
- // Delta related
- uint256 public deltaCredit; // credits accumulated from txn
- bool public batched; // flag to indicate if we want batch processing.
- bool public defaultSwapMode; // flag for the default mode for swap
- bool public defaultLPMode; // flag for the default mode for lp
- uint256 public swapDeltaBP; // basis points of poolCredits to activate Delta in swap
- uint256 public lpDeltaBP; // basis points of poolCredits to activate Delta in liquidity events
- //---------------------------------------------------------------------------
- // EVENTS
- event Mint(address to, uint256 amountLP, uint256 amountSD, uint256 mintFeeAmountSD);
- event Burn(address from, uint256 amountLP, uint256 amountSD);
- event RedeemLocalCallback(address _to, uint256 _amountSD, uint256 _amountToMintSD);
- event Swap(
- uint16 chainId,
- uint256 dstPoolId,
- address from,
- uint256 amountSD,
- uint256 eqReward,
- uint256 eqFee,
- uint256 protocolFee,
- uint256 lpFee
- );
- event SendCredits(uint16 dstChainId, uint256 dstPoolId, uint256 credits, uint256 idealBalance);
- event RedeemRemote(uint16 chainId, uint256 dstPoolId, address from, uint256 amountLP, uint256 amountSD);
- event RedeemLocal(address from, uint256 amountLP, uint256 amountSD, uint16 chainId, uint256 dstPoolId, bytes to);
- event InstantRedeemLocal(address from, uint256 amountLP, uint256 amountSD, address to);
- event CreditChainPath(uint16 chainId, uint256 srcPoolId, uint256 amountSD, uint256 idealBalance);
- event SwapRemote(address to, uint256 amountSD, uint256 protocolFee, uint256 dstFee);
- event WithdrawRemote(uint16 srcChainId, uint256 srcPoolId, uint256 swapAmount, uint256 mintAmount);
- event ChainPathUpdate(uint16 dstChainId, uint256 dstPoolId, uint256 weight);
- event FeesUpdated(uint256 mintFeeBP);
- event FeeLibraryUpdated(address feeLibraryAddr);
- event StopSwapUpdated(bool swapStop);
- event WithdrawProtocolFeeBalance(address to, uint256 amountSD);
- event WithdrawMintFeeBalance(address to, uint256 amountSD);
- event DeltaParamUpdated(bool batched, uint256 swapDeltaBP, uint256 lpDeltaBP, bool defaultSwapMode, bool defaultLPMode);
- //---------------------------------------------------------------------------
- // MODIFIERS
- modifier onlyRouter() {
- require(msg.sender == router, "Mirrorgate: only the router can call this method");
- _;
- }
- constructor(
- uint256 _poolId,
- address _router,
- address _token,
- uint256 _sharedDecimals,
- uint256 _localDecimals,
- address _feeLibrary,
- string memory _name,
- string memory _symbol
- ) LPTokenERC20(_name, _symbol) {
- require(_token != address(0x0), "Mirrorgate: _token cannot be 0x0");
- require(_router != address(0x0), "Mirrorgate: _router cannot be 0x0");
- poolId = _poolId;
- router = _router;
- token = _token;
- sharedDecimals = _sharedDecimals;
- decimals = uint8(_sharedDecimals);
- localDecimals = _localDecimals;
- convertRate = 10**(uint256(localDecimals).sub(sharedDecimals));
- totalWeight = 0;
- feeLibrary = _feeLibrary;
- //delta algo related
- batched = false;
- defaultSwapMode = true;
- defaultLPMode = true;
- }
- function getChainPathsLength() public view returns (uint256) {
- return chainPaths.length;
- }
- //---------------------------------------------------------------------------
- // LOCAL CHAIN FUNCTIONS
- function mint(address _to, uint256 _amountLD) external nonReentrant onlyRouter returns (uint256) {
- return _mintLocal(_to, _amountLD, true, true);
- }
- // Local Remote
- // ------- ---------
- // swap -> swapRemote
- function swap(
- uint16 _dstChainId,
- uint256 _dstPoolId,
- address _from,
- uint256 _amountLD,
- uint256 _minAmountLD,
- bool newLiquidity
- ) external nonReentrant onlyRouter returns (SwapObj memory) {
- require(!stopSwap, "Mirrorgate: swap func stopped");
- ChainPath storage cp = getAndCheckCP(_dstChainId, _dstPoolId);
- require(cp.ready == true, "Mirrorgate: counter chainPath is not ready");
- uint256 amountSD = amountLDtoSD(_amountLD);
- uint256 minAmountSD = amountLDtoSD(_minAmountLD);
- // request fee params from library
- SwapObj memory s = IMirrorgateFeeLibrary(feeLibrary).getFees(poolId, _dstPoolId, _dstChainId, _from, amountSD);
- // equilibrium fee and reward. note eqFee/eqReward are separated from swap liquidity
- eqFeePool = eqFeePool.sub(s.eqReward);
- // update the new amount the user gets minus the fees
- s.amount = amountSD.sub(s.eqFee).sub(s.protocolFee).sub(s.lpFee);
- // users will also get the eqReward
- require(s.amount.add(s.eqReward) >= minAmountSD, "Mirrorgate: slippage too high");
- // behaviours
- // - protocolFee: booked, stayed and withdrawn at remote.
- // - eqFee: booked, stayed and withdrawn at remote.
- // - lpFee: booked and stayed at remote, can be withdrawn anywhere
- s.lkbRemove = amountSD.sub(s.lpFee).add(s.eqReward);
- // check for transfer solvency.
- require(cp.balance >= s.lkbRemove, "Mirrorgate: dst balance too low");
- cp.balance = cp.balance.sub(s.lkbRemove);
- if (newLiquidity) {
- deltaCredit = deltaCredit.add(amountSD).add(s.eqReward);
- } else if (s.eqReward > 0) {
- deltaCredit = deltaCredit.add(s.eqReward);
- }
- // distribute credits on condition.
- if (!batched || deltaCredit >= totalLiquidity.mul(swapDeltaBP).div(BP_DENOMINATOR)) {
- _delta(defaultSwapMode);
- }
- emit Swap(_dstChainId, _dstPoolId, _from, s.amount, s.eqReward, s.eqFee, s.protocolFee, s.lpFee);
- return s;
- }
- // Local Remote
- // ------- ---------
- // sendCredits -> creditChainPath
- function sendCredits(uint16 _dstChainId, uint256 _dstPoolId) external nonReentrant onlyRouter returns (CreditObj memory c) {
- ChainPath storage cp = getAndCheckCP(_dstChainId, _dstPoolId);
- require(cp.ready == true, "Mirrorgate: counter chainPath is not ready");
- cp.lkb = cp.lkb.add(cp.credits);
- c.idealBalance = totalLiquidity.mul(cp.weight).div(totalWeight);
- c.credits = cp.credits;
- cp.credits = 0;
- emit SendCredits(_dstChainId, _dstPoolId, c.credits, c.idealBalance);
- }
- // Local Remote
- // ------- ---------
- // redeemRemote -> swapRemote
- function redeemRemote(
- uint16 _dstChainId,
- uint256 _dstPoolId,
- address _from,
- uint256 _amountLP
- ) external nonReentrant onlyRouter {
- require(_from != address(0x0), "Mirrorgate: _from cannot be 0x0");
- uint256 amountSD = _burnLocal(_from, _amountLP);
- //run Delta
- if (!batched || deltaCredit > totalLiquidity.mul(lpDeltaBP).div(BP_DENOMINATOR)) {
- _delta(defaultLPMode);
- }
- uint256 amountLD = amountSDtoLD(amountSD);
- emit RedeemRemote(_dstChainId, _dstPoolId, _from, _amountLP, amountLD);
- }
- function instantRedeemLocal(
- address _from,
- uint256 _amountLP,
- address _to
- ) external nonReentrant onlyRouter returns (uint256 amountSD) {
- require(_from != address(0x0), "Mirrorgate: _from cannot be 0x0");
- uint256 _deltaCredit = deltaCredit; // sload optimization.
- uint256 _capAmountLP = _amountSDtoLP(_deltaCredit);
- if (_amountLP > _capAmountLP) _amountLP = _capAmountLP;
- amountSD = _burnLocal(_from, _amountLP);
- deltaCredit = _deltaCredit.sub(amountSD);
- uint256 amountLD = amountSDtoLD(amountSD);
- _safeTransfer(token, _to, amountLD);
- emit InstantRedeemLocal(_from, _amountLP, amountSD, _to);
- }
- // Local Remote
- // ------- ---------
- // redeemLocal -> redeemLocalCheckOnRemote
- // redeemLocalCallback <-
- function redeemLocal(
- address _from,
- uint256 _amountLP,
- uint16 _dstChainId,
- uint256 _dstPoolId,
- bytes calldata _to
- ) external nonReentrant onlyRouter returns (uint256 amountSD) {
- require(_from != address(0x0), "Mirrorgate: _from cannot be 0x0");
- // safeguard.
- require(chainPaths[chainPathIndexLookup[_dstChainId][_dstPoolId]].ready == true, "Mirrorgate: counter chainPath is not ready");
- amountSD = _burnLocal(_from, _amountLP);
- // run Delta
- if (!batched || deltaCredit > totalLiquidity.mul(lpDeltaBP).div(BP_DENOMINATOR)) {
- _delta(false);
- }
- emit RedeemLocal(_from, _amountLP, amountSD, _dstChainId, _dstPoolId, _to);
- }
- //---------------------------------------------------------------------------
- // REMOTE CHAIN FUNCTIONS
- // Local Remote
- // ------- ---------
- // sendCredits -> creditChainPath
- function creditChainPath(
- uint16 _dstChainId,
- uint256 _dstPoolId,
- CreditObj memory _c
- ) external nonReentrant onlyRouter {
- ChainPath storage cp = chainPaths[chainPathIndexLookup[_dstChainId][_dstPoolId]];
- cp.balance = cp.balance.add(_c.credits);
- if (cp.idealBalance != _c.idealBalance) {
- cp.idealBalance = _c.idealBalance;
- }
- emit CreditChainPath(_dstChainId, _dstPoolId, _c.credits, _c.idealBalance);
- }
- // Local Remote
- // ------- ---------
- // swap -> swapRemote
- function swapRemote(
- uint16 _srcChainId,
- uint256 _srcPoolId,
- address _to,
- SwapObj memory _s
- ) external nonReentrant onlyRouter returns (uint256 amountLD) {
- // booking lpFee
- totalLiquidity = totalLiquidity.add(_s.lpFee);
- // booking eqFee
- eqFeePool = eqFeePool.add(_s.eqFee);
- // booking stargateFee
- protocolFeeBalance = protocolFeeBalance.add(_s.protocolFee);
- // update LKB
- uint256 chainPathIndex = chainPathIndexLookup[_srcChainId][_srcPoolId];
- chainPaths[chainPathIndex].lkb = chainPaths[chainPathIndex].lkb.sub(_s.lkbRemove);
- // user receives the amount + the srcReward
- amountLD = amountSDtoLD(_s.amount.add(_s.eqReward));
- _safeTransfer(token, _to, amountLD);
- emit SwapRemote(_to, _s.amount.add(_s.eqReward), _s.protocolFee, _s.eqFee);
- }
- // Local Remote
- // ------- ---------
- // redeemLocal -> redeemLocalCheckOnRemote
- // redeemLocalCallback <-
- function redeemLocalCallback(
- uint16 _srcChainId,
- uint256 _srcPoolId,
- address _to,
- uint256 _amountSD,
- uint256 _amountToMintSD
- ) external nonReentrant onlyRouter {
- if (_amountToMintSD > 0) {
- _mintLocal(_to, amountSDtoLD(_amountToMintSD), false, false);
- }
- ChainPath storage cp = getAndCheckCP(_srcChainId, _srcPoolId);
- cp.lkb = cp.lkb.sub(_amountSD);
- uint256 amountLD = amountSDtoLD(_amountSD);
- _safeTransfer(token, _to, amountLD);
- emit RedeemLocalCallback(_to, _amountSD, _amountToMintSD);
- }
- // Local Remote
- // ------- ---------
- // redeemLocal(amount) -> redeemLocalCheckOnRemote
- // redeemLocalCallback <-
- function redeemLocalCheckOnRemote(
- uint16 _srcChainId,
- uint256 _srcPoolId,
- uint256 _amountSD
- ) external nonReentrant onlyRouter returns (uint256 swapAmount, uint256 mintAmount) {
- ChainPath storage cp = getAndCheckCP(_srcChainId, _srcPoolId);
- if (_amountSD > cp.balance) {
- mintAmount = _amountSD - cp.balance;
- swapAmount = cp.balance;
- cp.balance = 0;
- } else {
- cp.balance = cp.balance.sub(_amountSD);
- swapAmount = _amountSD;
- mintAmount = 0;
- }
- emit WithdrawRemote(_srcChainId, _srcPoolId, swapAmount, mintAmount);
- }
- //---------------------------------------------------------------------------
- // DAO Calls
- function createChainPath(
- uint16 _dstChainId,
- uint256 _dstPoolId,
- uint256 _weight
- ) external onlyRouter {
- for (uint256 i = 0; i < chainPaths.length; ++i) {
- ChainPath memory cp = chainPaths[i];
- bool exists = cp.dstChainId == _dstChainId && cp.dstPoolId == _dstPoolId;
- require(!exists, "Mirrorgate: cant createChainPath of existing dstChainId and _dstPoolId");
- }
- totalWeight = totalWeight.add(_weight);
- chainPathIndexLookup[_dstChainId][_dstPoolId] = chainPaths.length;
- chainPaths.push(ChainPath(false, _dstChainId, _dstPoolId, _weight, 0, 0, 0, 0));
- emit ChainPathUpdate(_dstChainId, _dstPoolId, _weight);
- }
- function setWeightForChainPath(
- uint16 _dstChainId,
- uint256 _dstPoolId,
- uint16 _weight
- ) external onlyRouter {
- ChainPath storage cp = getAndCheckCP(_dstChainId, _dstPoolId);
- totalWeight = totalWeight.sub(cp.weight).add(_weight);
- cp.weight = _weight;
- emit ChainPathUpdate(_dstChainId, _dstPoolId, _weight);
- }
- function setFee(uint256 _mintFeeBP) external onlyRouter {
- require(_mintFeeBP <= BP_DENOMINATOR, "Bridge: cum fees > 100%");
- mintFeeBP = _mintFeeBP;
- emit FeesUpdated(mintFeeBP);
- }
- function setFeeLibrary(address _feeLibraryAddr) external onlyRouter {
- require(_feeLibraryAddr != address(0x0), "Mirrorgate: fee library cant be 0x0");
- feeLibrary = _feeLibraryAddr;
- emit FeeLibraryUpdated(_feeLibraryAddr);
- }
- function setSwapStop(bool _swapStop) external onlyRouter {
- stopSwap = _swapStop;
- emit StopSwapUpdated(_swapStop);
- }
- function setDeltaParam(
- bool _batched,
- uint256 _swapDeltaBP,
- uint256 _lpDeltaBP,
- bool _defaultSwapMode,
- bool _defaultLPMode
- ) external onlyRouter {
- require(_swapDeltaBP <= BP_DENOMINATOR && _lpDeltaBP <= BP_DENOMINATOR, "Mirrorgate: wrong Delta param");
- batched = _batched;
- swapDeltaBP = _swapDeltaBP;
- lpDeltaBP = _lpDeltaBP;
- defaultSwapMode = _defaultSwapMode;
- defaultLPMode = _defaultLPMode;
- emit DeltaParamUpdated(_batched, _swapDeltaBP, _lpDeltaBP, _defaultSwapMode, _defaultLPMode);
- }
- function callDelta(bool _fullMode) external onlyRouter {
- _delta(_fullMode);
- }
- function activateChainPath(uint16 _dstChainId, uint256 _dstPoolId) external onlyRouter {
- ChainPath storage cp = getAndCheckCP(_dstChainId, _dstPoolId);
- require(cp.ready == false, "Mirrorgate: chainPath is already active");
- // this func will only be called once
- cp.ready = true;
- }
- function withdrawProtocolFeeBalance(address _to) external onlyRouter {
- if (protocolFeeBalance > 0) {
- uint256 amountOfLD = amountSDtoLD(protocolFeeBalance);
- protocolFeeBalance = 0;
- _safeTransfer(token, _to, amountOfLD);
- emit WithdrawProtocolFeeBalance(_to, amountOfLD);
- }
- }
- function withdrawMintFeeBalance(address _to) external onlyRouter {
- if (mintFeeBalance > 0) {
- uint256 amountOfLD = amountSDtoLD(mintFeeBalance);
- mintFeeBalance = 0;
- _safeTransfer(token, _to, amountOfLD);
- emit WithdrawMintFeeBalance(_to, amountOfLD);
- }
- }
- //---------------------------------------------------------------------------
- // INTERNAL
- // Conversion Helpers
- //---------------------------------------------------------------------------
- function amountLPtoLD(uint256 _amountLP) external view returns (uint256) {
- return amountSDtoLD(_amountLPtoSD(_amountLP));
- }
- function _amountLPtoSD(uint256 _amountLP) internal view returns (uint256) {
- require(totalSupply > 0, "Mirrorgate: cant convert LPtoSD when totalSupply == 0");
- return _amountLP.mul(totalLiquidity).div(totalSupply);
- }
- function _amountSDtoLP(uint256 _amountSD) internal view returns (uint256) {
- require(totalLiquidity > 0, "Mirrorgate: cant convert SDtoLP when totalLiq == 0");
- return _amountSD.mul(totalSupply).div(totalLiquidity);
- }
- function amountSDtoLD(uint256 _amount) internal view returns (uint256) {
- return _amount.mul(convertRate);
- }
- function amountLDtoSD(uint256 _amount) internal view returns (uint256) {
- return _amount.div(convertRate);
- }
- function getAndCheckCP(uint16 _dstChainId, uint256 _dstPoolId) internal view returns (ChainPath storage) {
- require(chainPaths.length > 0, "Mirrorgate: no chainpaths exist");
- ChainPath storage cp = chainPaths[chainPathIndexLookup[_dstChainId][_dstPoolId]];
- require(cp.dstChainId == _dstChainId && cp.dstPoolId == _dstPoolId, "Mirrorgate: local chainPath does not exist");
- return cp;
- }
- function getChainPath(uint16 _dstChainId, uint256 _dstPoolId) external view returns (ChainPath memory) {
- ChainPath memory cp = chainPaths[chainPathIndexLookup[_dstChainId][_dstPoolId]];
- require(cp.dstChainId == _dstChainId && cp.dstPoolId == _dstPoolId, "Mirrorgate: local chainPath does not exist");
- return cp;
- }
- function _burnLocal(address _from, uint256 _amountLP) internal returns (uint256) {
- require(totalSupply > 0, "Mirrorgate: cant burn when totalSupply == 0");
- uint256 amountOfLPTokens = balanceOf[_from];
- require(amountOfLPTokens >= _amountLP, "Mirrorgate: not enough LP tokens to burn");
- uint256 amountSD = _amountLP.mul(totalLiquidity).div(totalSupply);
- //subtract totalLiquidity accordingly
- totalLiquidity = totalLiquidity.sub(amountSD);
- _burn(_from, _amountLP);
- emit Burn(_from, _amountLP, amountSD);
- return amountSD;
- }
- function _delta(bool fullMode) internal {
- if (deltaCredit > 0 && totalWeight > 0) {
- uint256 cpLength = chainPaths.length;
- uint256[] memory deficit = new uint256[](cpLength);
- uint256 totalDeficit = 0;
- // algorithm steps 6-9: calculate the total and the amounts required to get to balance state
- for (uint256 i = 0; i < cpLength; ++i) {
- ChainPath storage cp = chainPaths[i];
- // (liquidity * (weight/totalWeight)) - (lkb+credits)
- uint256 balLiq = totalLiquidity.mul(cp.weight).div(totalWeight);
- uint256 currLiq = cp.lkb.add(cp.credits);
- if (balLiq > currLiq) {
- // save gas since we know balLiq > currLiq and we know deficit[i] > 0
- deficit[i] = balLiq - currLiq;
- totalDeficit = totalDeficit.add(deficit[i]);
- }
- }
- // indicates how much delta credit is distributed
- uint256 spent;
- // handle credits with 2 tranches. the [ < totalDeficit] [excessCredit]
- // run full Delta, allocate all credits
- if (totalDeficit == 0) {
- // only fullMode delta will allocate excess credits
- if (fullMode && deltaCredit > 0) {
- // credit ChainPath by weights
- for (uint256 i = 0; i < cpLength; ++i) {
- ChainPath storage cp = chainPaths[i];
- // credits = credits + toBalanceChange + remaining allocation based on weight
- uint256 amtToCredit = deltaCredit.mul(cp.weight).div(totalWeight);
- spent = spent.add(amtToCredit);
- cp.credits = cp.credits.add(amtToCredit);
- }
- } // else do nth
- } else if (totalDeficit <= deltaCredit) {
- if (fullMode) {
- // algorithm step 13: calculate amount to disperse to bring to balance state or as close as possible
- uint256 excessCredit = deltaCredit - totalDeficit;
- // algorithm steps 14-16: calculate credits
- for (uint256 i = 0; i < cpLength; ++i) {
- if (deficit[i] > 0) {
- ChainPath storage cp = chainPaths[i];
- // credits = credits + deficit + remaining allocation based on weight
- uint256 amtToCredit = deficit[i].add(excessCredit.mul(cp.weight).div(totalWeight));
- spent = spent.add(amtToCredit);
- cp.credits = cp.credits.add(amtToCredit);
- }
- }
- } else {
- // totalDeficit <= deltaCredit but not running fullMode
- // credit chainPaths as is if any deficit, not using all deltaCredit
- for (uint256 i = 0; i < cpLength; ++i) {
- if (deficit[i] > 0) {
- ChainPath storage cp = chainPaths[i];
- uint256 amtToCredit = deficit[i];
- spent = spent.add(amtToCredit);
- cp.credits = cp.credits.add(amtToCredit);
- }
- }
- }
- } else {
- // totalDeficit > deltaCredit, fullMode or not, normalize the deficit by deltaCredit
- for (uint256 i = 0; i < cpLength; ++i) {
- if (deficit[i] > 0) {
- ChainPath storage cp = chainPaths[i];
- uint256 proportionalDeficit = deficit[i].mul(deltaCredit).div(totalDeficit);
- spent = spent.add(proportionalDeficit);
- cp.credits = cp.credits.add(proportionalDeficit);
- }
- }
- }
- // deduct the amount of credit sent
- deltaCredit = deltaCredit.sub(spent);
- }
- }
- function _mintLocal(
- address _to,
- uint256 _amountLD,
- bool _feesEnabled,
- bool _creditDelta
- ) internal returns (uint256 amountSD) {
- require(totalWeight > 0, "Mirrorgate: No ChainPaths exist");
- amountSD = amountLDtoSD(_amountLD);
- uint256 mintFeeSD = 0;
- if (_feesEnabled) {
- mintFeeSD = amountSD.mul(mintFeeBP).div(BP_DENOMINATOR);
- amountSD = amountSD.sub(mintFeeSD);
- mintFeeBalance = mintFeeBalance.add(mintFeeSD);
- }
- if (_creditDelta) {
- deltaCredit = deltaCredit.add(amountSD);
- }
- uint256 amountLPTokens = amountSD;
- if (totalSupply != 0) {
- amountLPTokens = amountSD.mul(totalSupply).div(totalLiquidity);
- }
- totalLiquidity = totalLiquidity.add(amountSD);
- _mint(_to, amountLPTokens);
- emit Mint(_to, amountLPTokens, amountSD, mintFeeSD);
- // add to credits and call delta. short circuit to save gas
- if (!batched || deltaCredit > totalLiquidity.mul(lpDeltaBP).div(BP_DENOMINATOR)) {
- _delta(defaultLPMode);
- }
- }
- function _safeTransfer(
- address _token,
- address _to,
- uint256 _value
- ) private {
- (bool success, bytes memory data) = _token.call(abi.encodeWithSelector(SELECTOR, _to, _value));
- require(success && (data.length == 0 || abi.decode(data, (bool))), "Mirrorgate: TRANSFER_FAILED");
- }
- }
|