123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644 |
- pragma solidity 0.7.6;
- pragma abicoder v2;
- import "@openzeppelin/contracts/access/Ownable.sol";
- import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
- import "./LPTokenERC20.sol";
- import "./interfaces/IStargateFeeLibrary.sol";
- import "@openzeppelin/contracts/math/SafeMath.sol";
- contract Pool is LPTokenERC20, ReentrancyGuard {
- using SafeMath for uint256;
-
-
- bytes4 private constant SELECTOR = bytes4(keccak256(bytes("transfer(address,uint256)")));
- uint256 public constant BP_DENOMINATOR = 10000;
-
-
- struct ChainPath {
- bool ready;
- 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;
- }
-
-
-
- ChainPath[] public chainPaths;
- mapping(uint16 => mapping(uint256 => uint256)) public chainPathIndexLookup;
-
- uint256 public immutable poolId;
- uint256 public sharedDecimals;
- uint256 public localDecimals;
- uint256 public immutable convertRate;
- address public immutable token;
- address public immutable router;
- bool public stopSwap;
-
- uint256 public totalLiquidity;
- uint256 public totalWeight;
- uint256 public mintFeeBP;
- uint256 public protocolFeeBalance;
- uint256 public mintFeeBalance;
- uint256 public eqFeePool;
- address public feeLibrary;
-
- uint256 public deltaCredit;
- bool public batched;
- bool public defaultSwapMode;
- bool public defaultLPMode;
- uint256 public swapDeltaBP;
- uint256 public lpDeltaBP;
-
-
- 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);
-
-
- modifier onlyRouter() {
- require(msg.sender == router, "Stargate: 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), "Stargate: _token cannot be 0x0");
- require(_router != address(0x0), "Stargate: _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;
-
- batched = false;
- defaultSwapMode = true;
- defaultLPMode = true;
- }
- function getChainPathsLength() public view returns (uint256) {
- return chainPaths.length;
- }
-
-
- function mint(address _to, uint256 _amountLD) external nonReentrant onlyRouter returns (uint256) {
- return _mintLocal(_to, _amountLD, true, true);
- }
-
-
-
- function swap(
- uint16 _dstChainId,
- uint256 _dstPoolId,
- address _from,
- uint256 _amountLD,
- uint256 _minAmountLD,
- bool newLiquidity
- ) external nonReentrant onlyRouter returns (SwapObj memory) {
- require(!stopSwap, "Stargate: swap func stopped");
- ChainPath storage cp = getAndCheckCP(_dstChainId, _dstPoolId);
- require(cp.ready == true, "Stargate: counter chainPath is not ready");
- uint256 amountSD = amountLDtoSD(_amountLD);
- uint256 minAmountSD = amountLDtoSD(_minAmountLD);
-
- SwapObj memory s = IStargateFeeLibrary(feeLibrary).getFees(poolId, _dstPoolId, _dstChainId, _from, amountSD);
-
- eqFeePool = eqFeePool.sub(s.eqReward);
-
- s.amount = amountSD.sub(s.eqFee).sub(s.protocolFee).sub(s.lpFee);
-
- require(s.amount.add(s.eqReward) >= minAmountSD, "Stargate: slippage too high");
-
-
-
-
- s.lkbRemove = amountSD.sub(s.lpFee).add(s.eqReward);
-
- require(cp.balance >= s.lkbRemove, "Stargate: 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);
- }
-
- 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;
- }
-
-
-
- function sendCredits(uint16 _dstChainId, uint256 _dstPoolId) external nonReentrant onlyRouter returns (CreditObj memory c) {
- ChainPath storage cp = getAndCheckCP(_dstChainId, _dstPoolId);
- require(cp.ready == true, "Stargate: 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);
- }
-
-
-
- function redeemRemote(
- uint16 _dstChainId,
- uint256 _dstPoolId,
- address _from,
- uint256 _amountLP
- ) external nonReentrant onlyRouter {
- require(_from != address(0x0), "Stargate: _from cannot be 0x0");
- uint256 amountSD = _burnLocal(_from, _amountLP);
-
- 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), "Stargate: _from cannot be 0x0");
- uint256 _deltaCredit = deltaCredit;
- 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);
- }
-
-
-
-
- function redeemLocal(
- address _from,
- uint256 _amountLP,
- uint16 _dstChainId,
- uint256 _dstPoolId,
- bytes calldata _to
- ) external nonReentrant onlyRouter returns (uint256 amountSD) {
- require(_from != address(0x0), "Stargate: _from cannot be 0x0");
-
- require(chainPaths[chainPathIndexLookup[_dstChainId][_dstPoolId]].ready == true, "Stargate: counter chainPath is not ready");
- amountSD = _burnLocal(_from, _amountLP);
-
- if (!batched || deltaCredit > totalLiquidity.mul(lpDeltaBP).div(BP_DENOMINATOR)) {
- _delta(false);
- }
- emit RedeemLocal(_from, _amountLP, amountSD, _dstChainId, _dstPoolId, _to);
- }
-
-
-
-
-
- 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);
- }
-
-
-
- function swapRemote(
- uint16 _srcChainId,
- uint256 _srcPoolId,
- address _to,
- SwapObj memory _s
- ) external nonReentrant onlyRouter returns (uint256 amountLD) {
-
- totalLiquidity = totalLiquidity.add(_s.lpFee);
-
- eqFeePool = eqFeePool.add(_s.eqFee);
-
- protocolFeeBalance = protocolFeeBalance.add(_s.protocolFee);
-
- uint256 chainPathIndex = chainPathIndexLookup[_srcChainId][_srcPoolId];
- chainPaths[chainPathIndex].lkb = chainPaths[chainPathIndex].lkb.sub(_s.lkbRemove);
-
- amountLD = amountSDtoLD(_s.amount.add(_s.eqReward));
- _safeTransfer(token, _to, amountLD);
- emit SwapRemote(_to, _s.amount.add(_s.eqReward), _s.protocolFee, _s.eqFee);
- }
-
-
-
-
- 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);
- }
-
-
-
-
- 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);
- }
-
-
- 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, "Stargate: 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), "Stargate: 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, "Stargate: 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, "Stargate: chainPath is already active");
-
- 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);
- }
- }
-
-
-
-
- function amountLPtoLD(uint256 _amountLP) external view returns (uint256) {
- return amountSDtoLD(_amountLPtoSD(_amountLP));
- }
- function _amountLPtoSD(uint256 _amountLP) internal view returns (uint256) {
- require(totalSupply > 0, "Stargate: cant convert LPtoSD when totalSupply == 0");
- return _amountLP.mul(totalLiquidity).div(totalSupply);
- }
- function _amountSDtoLP(uint256 _amountSD) internal view returns (uint256) {
- require(totalLiquidity > 0, "Stargate: 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, "Stargate: no chainpaths exist");
- ChainPath storage cp = chainPaths[chainPathIndexLookup[_dstChainId][_dstPoolId]];
- require(cp.dstChainId == _dstChainId && cp.dstPoolId == _dstPoolId, "Stargate: 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, "Stargate: local chainPath does not exist");
- return cp;
- }
- function _burnLocal(address _from, uint256 _amountLP) internal returns (uint256) {
- require(totalSupply > 0, "Stargate: cant burn when totalSupply == 0");
- uint256 amountOfLPTokens = balanceOf[_from];
- require(amountOfLPTokens >= _amountLP, "Stargate: not enough LP tokens to burn");
- uint256 amountSD = _amountLP.mul(totalLiquidity).div(totalSupply);
-
- 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;
-
- for (uint256 i = 0; i < cpLength; ++i) {
- ChainPath storage cp = chainPaths[i];
-
- uint256 balLiq = totalLiquidity.mul(cp.weight).div(totalWeight);
- uint256 currLiq = cp.lkb.add(cp.credits);
- if (balLiq > currLiq) {
-
- deficit[i] = balLiq - currLiq;
- totalDeficit = totalDeficit.add(deficit[i]);
- }
- }
-
- uint256 spent;
-
-
- if (totalDeficit == 0) {
-
- if (fullMode && deltaCredit > 0) {
-
- for (uint256 i = 0; i < cpLength; ++i) {
- ChainPath storage cp = chainPaths[i];
-
- uint256 amtToCredit = deltaCredit.mul(cp.weight).div(totalWeight);
- spent = spent.add(amtToCredit);
- cp.credits = cp.credits.add(amtToCredit);
- }
- }
- } else if (totalDeficit <= deltaCredit) {
- if (fullMode) {
-
- uint256 excessCredit = deltaCredit - totalDeficit;
-
- for (uint256 i = 0; i < cpLength; ++i) {
- if (deficit[i] > 0) {
- ChainPath storage cp = chainPaths[i];
-
- uint256 amtToCredit = deficit[i].add(excessCredit.mul(cp.weight).div(totalWeight));
- spent = spent.add(amtToCredit);
- cp.credits = cp.credits.add(amtToCredit);
- }
- }
- } else {
-
-
- 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 {
-
- 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);
- }
- }
- }
-
- deltaCredit = deltaCredit.sub(spent);
- }
- }
- function _mintLocal(
- address _to,
- uint256 _amountLD,
- bool _feesEnabled,
- bool _creditDelta
- ) internal returns (uint256 amountSD) {
- require(totalWeight > 0, "Stargate: 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);
-
- 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))), "Stargate: TRANSFER_FAILED");
- }
- }
|