// SPDX-License-Identifier: MITpragmasolidity >=0.6.0 <0.8.0;/*
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with GSN meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/abstractcontractContext{
function_msgSender() internalviewvirtualreturns (addresspayable) {
returnmsg.sender;
}
function_msgData() internalviewvirtualreturns (bytesmemory) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691returnmsg.data;
}
}
Contract Source Code
File 3 of 16: Factory.sol
// SPDX-License-Identifier: BUSL-1.1pragmasolidity 0.7.6;pragmaabicoderv2;import"@openzeppelin/contracts/math/SafeMath.sol";
import"@openzeppelin/contracts/access/Ownable.sol";
import"./Pool.sol";
contractFactoryisOwnable{
usingSafeMathforuint256;
//---------------------------------------------------------------------------// VARIABLESmapping(uint256=> Pool) public getPool; // poolId -> PoolInfoaddress[] public allPools;
addresspublicimmutable router;
addresspublic defaultFeeLibrary; // address for retrieving fee params for swaps//---------------------------------------------------------------------------// MODIFIERSmodifieronlyRouter() {
require(msg.sender== router, "Stargate: caller must be Router.");
_;
}
constructor(address _router) {
require(_router !=address(0x0), "Stargate: _router cant be 0x0"); // 1 time only
router = _router;
}
functionsetDefaultFeeLibrary(address _defaultFeeLibrary) externalonlyOwner{
require(_defaultFeeLibrary !=address(0x0), "Stargate: fee library cant be 0x0");
defaultFeeLibrary = _defaultFeeLibrary;
}
functionallPoolsLength() externalviewreturns (uint256) {
return allPools.length;
}
functioncreatePool(uint256 _poolId,
address _token,
uint8 _sharedDecimals,
uint8 _localDecimals,
stringmemory _name,
stringmemory _symbol
) publiconlyRouterreturns (address poolAddress) {
require(address(getPool[_poolId]) ==address(0x0), "Stargate: Pool already created");
Pool pool =new Pool(_poolId, router, _token, _sharedDecimals, _localDecimals, defaultFeeLibrary, _name, _symbol);
getPool[_poolId] = pool;
poolAddress =address(pool);
allPools.push(poolAddress);
}
functionrenounceOwnership() publicoverrideonlyOwner{}
}
Contract Source Code
File 4 of 16: IERC20.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.7.0;/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/interfaceIERC20{
/**
* @dev Returns the amount of tokens in existence.
*/functiontotalSupply() externalviewreturns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/functionbalanceOf(address account) externalviewreturns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/functiontransfer(address recipient, uint256 amount) externalreturns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/functionallowance(address owner, address spender) externalviewreturns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/functionapprove(address spender, uint256 amount) externalreturns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/functiontransferFrom(address sender, address recipient, uint256 amount) externalreturns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/eventTransfer(addressindexedfrom, addressindexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/eventApproval(addressindexed owner, addressindexed spender, uint256 value);
}
Contract Source Code
File 5 of 16: ILayerZeroEndpoint.sol
// SPDX-License-Identifier: BUSL-1.1pragmasolidity >=0.5.0;import"./ILayerZeroUserApplicationConfig.sol";
interfaceILayerZeroEndpointisILayerZeroUserApplicationConfig{
// @notice send a LayerZero message to the specified address at a LayerZero endpoint.// @param _dstChainId - the destination chain identifier// @param _destination - the address on destination chain (in bytes). address length/format may vary by chains// @param _payload - a custom bytes payload to send to the destination contract// @param _refundAddress - if the source transaction is cheaper than the amount of value passed, refund the additional amount to this address// @param _zroPaymentAddress - the address of the ZRO token holder who would pay for the transaction// @param _adapterParams - parameters for custom functionality. ie: pay for a specified destination gasAmount, or receive airdropped native gas from the relayer on destinationfunctionsend(uint16 _dstChainId, bytescalldata _destination, bytescalldata _payload, addresspayable _refundAddress, address _zroPaymentAddress, bytescalldata _adapterParams) externalpayable;
// @notice used by the messaging library to publish verified payload// @param _srcChainId - the source chain identifier// @param _srcAddress - the source contract (as bytes) at the source chain// @param _dstAddress - the address on destination chain// @param _nonce - the unbound message ordering nonce// @param _gasLimit - the gas limit for external contract execution// @param _payload - verified payload to send to the destination contractfunctionreceivePayload(uint16 _srcChainId, bytescalldata _srcAddress, address _dstAddress, uint64 _nonce, uint _gasLimit, bytescalldata _payload) external;
// @notice get the inboundNonce of a receiver from a source chain which could be EVM or non-EVM chain// @param _srcChainId - the source chain identifier// @param _srcAddress - the source chain contract addressfunctiongetInboundNonce(uint16 _srcChainId, bytescalldata _srcAddress) externalviewreturns (uint64);
// @notice get the outboundNonce from this source chain which, consequently, is always an EVM// @param _srcAddress - the source chain contract addressfunctiongetOutboundNonce(uint16 _dstChainId, address _srcAddress) externalviewreturns (uint64);
// @notice gets a quote in source native gas, for the amount that send() requires to pay for message delivery// @param _dstChainId - the destination chain identifier// @param _userApplication - the user app address on this EVM chain// @param _payload - the custom message to send over LayerZero// @param _payInZRO - if false, user app pays the protocol fee in native token// @param _adapterParam - parameters for the adapter service, e.g. send some dust native token to dstChainfunctionestimateFees(uint16 _dstChainId, address _userApplication, bytescalldata _payload, bool _payInZRO, bytescalldata _adapterParam) externalviewreturns (uint nativeFee, uint zroFee);
// @notice get this Endpoint's immutable source identifierfunctiongetChainId() externalviewreturns (uint16);
// @notice the interface to retry failed message on this Endpoint destination// @param _srcChainId - the source chain identifier// @param _srcAddress - the source chain contract address// @param _payload - the payload to be retriedfunctionretryPayload(uint16 _srcChainId, bytescalldata _srcAddress, bytescalldata _payload) external;
// @notice query if any STORED payload (message blocking) at the endpoint.// @param _srcChainId - the source chain identifier// @param _srcAddress - the source chain contract addressfunctionhasStoredPayload(uint16 _srcChainId, bytescalldata _srcAddress) externalviewreturns (bool);
// @notice query if the _libraryAddress is valid for sending msgs.// @param _userApplication - the user app address on this EVM chainfunctiongetSendLibraryAddress(address _userApplication) externalviewreturns (address);
// @notice query if the _libraryAddress is valid for receiving msgs.// @param _userApplication - the user app address on this EVM chainfunctiongetReceiveLibraryAddress(address _userApplication) externalviewreturns (address);
// @notice query if the non-reentrancy guard for send() is on// @return true if the guard is on. false otherwisefunctionisSendingPayload() externalviewreturns (bool);
// @notice query if the non-reentrancy guard for receive() is on// @return true if the guard is on. false otherwisefunctionisReceivingPayload() externalviewreturns (bool);
// @notice get the configuration of the LayerZero messaging library of the specified version// @param _version - messaging library version// @param _chainId - the chainId for the pending config change// @param _userApplication - the contract address of the user application// @param _configType - type of configuration. every messaging library has its own convention.functiongetConfig(uint16 _version, uint16 _chainId, address _userApplication, uint _configType) externalviewreturns (bytesmemory);
// @notice get the send() LayerZero messaging library version// @param _userApplication - the contract address of the user applicationfunctiongetSendVersion(address _userApplication) externalviewreturns (uint16);
// @notice get the lzReceive() LayerZero messaging library version// @param _userApplication - the contract address of the user applicationfunctiongetReceiveVersion(address _userApplication) externalviewreturns (uint16);
}
Contract Source Code
File 6 of 16: ILayerZeroReceiver.sol
// SPDX-License-Identifier: BUSL-1.1pragmasolidity >=0.5.0;interfaceILayerZeroReceiver{
// @notice LayerZero endpoint will invoke this function to deliver the message on the destination// @param _srcChainId - the source endpoint identifier// @param _srcAddress - the source sending contract address from the source chain// @param _nonce - the ordered message nonce// @param _payload - the signed payload is the UA bytes has encoded to be sentfunctionlzReceive(uint16 _srcChainId, bytescalldata _srcAddress, uint64 _nonce, bytescalldata _payload) external;
}
Contract Source Code
File 7 of 16: ILayerZeroUserApplicationConfig.sol
// SPDX-License-Identifier: BUSL-1.1pragmasolidity >=0.5.0;interfaceILayerZeroUserApplicationConfig{
// @notice set the configuration of the LayerZero messaging library of the specified version// @param _version - messaging library version// @param _chainId - the chainId for the pending config change// @param _configType - type of configuration. every messaging library has its own convention.// @param _config - configuration in the bytes. can encode arbitrary content.functionsetConfig(uint16 _version, uint16 _chainId, uint _configType, bytescalldata _config) external;
// @notice set the send() LayerZero messaging library version to _version// @param _version - new messaging library versionfunctionsetSendVersion(uint16 _version) external;
// @notice set the lzReceive() LayerZero messaging library version to _version// @param _version - new messaging library versionfunctionsetReceiveVersion(uint16 _version) external;
// @notice Only when the UA needs to resume the message flow in blocking mode and clear the stored payload// @param _srcChainId - the chainId of the source chain// @param _srcAddress - the contract address of the source contract at the source chainfunctionforceResumeReceive(uint16 _srcChainId, bytescalldata _srcAddress) external;
}
// SPDX-License-Identifier: MITpragmasolidity ^0.7.0;import"../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/abstractcontractOwnableisContext{
addressprivate _owner;
eventOwnershipTransferred(addressindexed previousOwner, addressindexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/constructor () {
address msgSender = _msgSender();
_owner = msgSender;
emit OwnershipTransferred(address(0), msgSender);
}
/**
* @dev Returns the address of the current owner.
*/functionowner() publicviewvirtualreturns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/modifieronlyOwner() {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/functionrenounceOwnership() publicvirtualonlyOwner{
emit OwnershipTransferred(_owner, address(0));
_owner =address(0);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/functiontransferOwnership(address newOwner) publicvirtualonlyOwner{
require(newOwner !=address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
Contract Source Code
File 13 of 16: Pool.sol
// SPDX-License-Identifier: BUSL-1.1pragmasolidity 0.7.6;pragmaabicoderv2;// importsimport"@openzeppelin/contracts/access/Ownable.sol";
import"@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import"./LPTokenERC20.sol";
import"./interfaces/IStargateFeeLibrary.sol";
// librariesimport"@openzeppelin/contracts/math/SafeMath.sol";
/// Pool contracts on other chains and managed by the Stargate protocol.contractPoolisLPTokenERC20, ReentrancyGuard{
usingSafeMathforuint256;
//---------------------------------------------------------------------------// CONSTANTSbytes4privateconstant SELECTOR =bytes4(keccak256(bytes("transfer(address,uint256)")));
uint256publicconstant BP_DENOMINATOR =10000;
//---------------------------------------------------------------------------// STRUCTSstructChainPath {
bool ready; // indicate if the counter chainPath has been created.uint16 dstChainId;
uint256 dstPoolId;
uint256 weight;
uint256 balance;
uint256 lkb;
uint256 credits;
uint256 idealBalance;
}
structSwapObj {
uint256 amount;
uint256 eqFee;
uint256 eqReward;
uint256 lpFee;
uint256 protocolFee;
uint256 lkbRemove;
}
structCreditObj {
uint256 credits;
uint256 idealBalance;
}
//---------------------------------------------------------------------------// VARIABLES// chainPath
ChainPath[] public chainPaths; // list of connected chains with shared poolsmapping(uint16=>mapping(uint256=>uint256)) public chainPathIndexLookup; // lookup for chainPath by chainId => poolId =>index// metadatauint256publicimmutable poolId; // shared id between chains to represent same pooluint256public sharedDecimals; // the shared decimals (lowest common decimals between chains)uint256public localDecimals; // the decimals for the tokenuint256publicimmutable convertRate; // the decimals for the tokenaddresspublicimmutable token; // the token for the pooladdresspublicimmutable router; // the token for the poolboolpublic stopSwap; // flag to stop swapping in extreme cases// Fee and Liquidityuint256public totalLiquidity; // the total amount of tokens added on this side of the chain (fees + deposits - withdrawals)uint256public totalWeight; // total weight for pool percentagesuint256public mintFeeBP; // fee basis points for the mint/deposituint256public protocolFeeBalance; // fee balance created from dao feeuint256public mintFeeBalance; // fee balance created from mint feeuint256public eqFeePool; // pool rewards in Shared Decimal format. indicate the total budget for reverse swap incentiveaddresspublic feeLibrary; // address for retrieving fee params for swaps// Delta relateduint256public deltaCredit; // credits accumulated from txnboolpublic batched; // flag to indicate if we want batch processing.boolpublic defaultSwapMode; // flag for the default mode for swapboolpublic defaultLPMode; // flag for the default mode for lpuint256public swapDeltaBP; // basis points of poolCredits to activate Delta in swapuint256public lpDeltaBP; // basis points of poolCredits to activate Delta in liquidity events//---------------------------------------------------------------------------// EVENTSeventMint(address to, uint256 amountLP, uint256 amountSD, uint256 mintFeeAmountSD);
eventBurn(addressfrom, uint256 amountLP, uint256 amountSD);
eventRedeemLocalCallback(address _to, uint256 _amountSD, uint256 _amountToMintSD);
eventSwap(uint16 chainId,
uint256 dstPoolId,
addressfrom,
uint256 amountSD,
uint256 eqReward,
uint256 eqFee,
uint256 protocolFee,
uint256 lpFee
);
eventSendCredits(uint16 dstChainId, uint256 dstPoolId, uint256 credits, uint256 idealBalance);
eventRedeemRemote(uint16 chainId, uint256 dstPoolId, addressfrom, uint256 amountLP, uint256 amountSD);
eventRedeemLocal(addressfrom, uint256 amountLP, uint256 amountSD, uint16 chainId, uint256 dstPoolId, bytes to);
eventInstantRedeemLocal(addressfrom, uint256 amountLP, uint256 amountSD, address to);
eventCreditChainPath(uint16 chainId, uint256 srcPoolId, uint256 amountSD, uint256 idealBalance);
eventSwapRemote(address to, uint256 amountSD, uint256 protocolFee, uint256 dstFee);
eventWithdrawRemote(uint16 srcChainId, uint256 srcPoolId, uint256 swapAmount, uint256 mintAmount);
eventChainPathUpdate(uint16 dstChainId, uint256 dstPoolId, uint256 weight);
eventFeesUpdated(uint256 mintFeeBP);
eventFeeLibraryUpdated(address feeLibraryAddr);
eventStopSwapUpdated(bool swapStop);
eventWithdrawProtocolFeeBalance(address to, uint256 amountSD);
eventWithdrawMintFeeBalance(address to, uint256 amountSD);
eventDeltaParamUpdated(bool batched, uint256 swapDeltaBP, uint256 lpDeltaBP, bool defaultSwapMode, bool defaultLPMode);
//---------------------------------------------------------------------------// MODIFIERSmodifieronlyRouter() {
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,
stringmemory _name,
stringmemory _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;
//delta algo related
batched =false;
defaultSwapMode =true;
defaultLPMode =true;
}
functiongetChainPathsLength() publicviewreturns (uint256) {
return chainPaths.length;
}
//---------------------------------------------------------------------------// LOCAL CHAIN FUNCTIONSfunctionmint(address _to, uint256 _amountLD) externalnonReentrantonlyRouterreturns (uint256) {
return _mintLocal(_to, _amountLD, true, true);
}
// Local Remote// ------- ---------// swap -> swapRemotefunctionswap(uint16 _dstChainId,
uint256 _dstPoolId,
address _from,
uint256 _amountLD,
uint256 _minAmountLD,
bool newLiquidity
) externalnonReentrantonlyRouterreturns (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);
// request fee params from library
SwapObj memory s = IStargateFeeLibrary(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 eqRewardrequire(s.amount.add(s.eqReward) >= minAmountSD, "Stargate: 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, "Stargate: dst balance too low");
cp.balance= cp.balance.sub(s.lkbRemove);
if (newLiquidity) {
deltaCredit = deltaCredit.add(amountSD).add(s.eqReward);
} elseif (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 -> creditChainPathfunctionsendCredits(uint16 _dstChainId, uint256 _dstPoolId) externalnonReentrantonlyRouterreturns (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);
}
// Local Remote// ------- ---------// redeemRemote -> swapRemotefunctionredeemRemote(uint16 _dstChainId,
uint256 _dstPoolId,
address _from,
uint256 _amountLP
) externalnonReentrantonlyRouter{
require(_from !=address(0x0), "Stargate: _from cannot be 0x0");
uint256 amountSD = _burnLocal(_from, _amountLP);
//run Deltaif (!batched || deltaCredit > totalLiquidity.mul(lpDeltaBP).div(BP_DENOMINATOR)) {
_delta(defaultLPMode);
}
uint256 amountLD = amountSDtoLD(amountSD);
emit RedeemRemote(_dstChainId, _dstPoolId, _from, _amountLP, amountLD);
}
functioninstantRedeemLocal(address _from,
uint256 _amountLP,
address _to
) externalnonReentrantonlyRouterreturns (uint256 amountSD) {
require(_from !=address(0x0), "Stargate: _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 <-functionredeemLocal(address _from,
uint256 _amountLP,
uint16 _dstChainId,
uint256 _dstPoolId,
bytescalldata _to
) externalnonReentrantonlyRouterreturns (uint256 amountSD) {
require(_from !=address(0x0), "Stargate: _from cannot be 0x0");
// safeguard.require(chainPaths[chainPathIndexLookup[_dstChainId][_dstPoolId]].ready ==true, "Stargate: counter chainPath is not ready");
amountSD = _burnLocal(_from, _amountLP);
// run Deltaif (!batched || deltaCredit > totalLiquidity.mul(lpDeltaBP).div(BP_DENOMINATOR)) {
_delta(false);
}
emit RedeemLocal(_from, _amountLP, amountSD, _dstChainId, _dstPoolId, _to);
}
//---------------------------------------------------------------------------// REMOTE CHAIN FUNCTIONS// Local Remote// ------- ---------// sendCredits -> creditChainPathfunctioncreditChainPath(uint16 _dstChainId,
uint256 _dstPoolId,
CreditObj memory _c
) externalnonReentrantonlyRouter{
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 -> swapRemotefunctionswapRemote(uint16 _srcChainId,
uint256 _srcPoolId,
address _to,
SwapObj memory _s
) externalnonReentrantonlyRouterreturns (uint256 amountLD) {
// booking lpFee
totalLiquidity = totalLiquidity.add(_s.lpFee);
// booking eqFee
eqFeePool = eqFeePool.add(_s.eqFee);
// booking stargateFee
protocolFeeBalance = protocolFeeBalance.add(_s.protocolFee);
// update LKBuint256 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 <-functionredeemLocalCallback(uint16 _srcChainId,
uint256 _srcPoolId,
address _to,
uint256 _amountSD,
uint256 _amountToMintSD
) externalnonReentrantonlyRouter{
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 <-functionredeemLocalCheckOnRemote(uint16 _srcChainId,
uint256 _srcPoolId,
uint256 _amountSD
) externalnonReentrantonlyRouterreturns (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 CallsfunctioncreateChainPath(uint16 _dstChainId,
uint256 _dstPoolId,
uint256 _weight
) externalonlyRouter{
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);
}
functionsetWeightForChainPath(uint16 _dstChainId,
uint256 _dstPoolId,
uint16 _weight
) externalonlyRouter{
ChainPath storage cp = getAndCheckCP(_dstChainId, _dstPoolId);
totalWeight = totalWeight.sub(cp.weight).add(_weight);
cp.weight = _weight;
emit ChainPathUpdate(_dstChainId, _dstPoolId, _weight);
}
functionsetFee(uint256 _mintFeeBP) externalonlyRouter{
require(_mintFeeBP <= BP_DENOMINATOR, "Bridge: cum fees > 100%");
mintFeeBP = _mintFeeBP;
emit FeesUpdated(mintFeeBP);
}
functionsetFeeLibrary(address _feeLibraryAddr) externalonlyRouter{
require(_feeLibraryAddr !=address(0x0), "Stargate: fee library cant be 0x0");
feeLibrary = _feeLibraryAddr;
emit FeeLibraryUpdated(_feeLibraryAddr);
}
functionsetSwapStop(bool _swapStop) externalonlyRouter{
stopSwap = _swapStop;
emit StopSwapUpdated(_swapStop);
}
functionsetDeltaParam(bool _batched,
uint256 _swapDeltaBP,
uint256 _lpDeltaBP,
bool _defaultSwapMode,
bool _defaultLPMode
) externalonlyRouter{
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);
}
functioncallDelta(bool _fullMode) externalonlyRouter{
_delta(_fullMode);
}
functionactivateChainPath(uint16 _dstChainId, uint256 _dstPoolId) externalonlyRouter{
ChainPath storage cp = getAndCheckCP(_dstChainId, _dstPoolId);
require(cp.ready ==false, "Stargate: chainPath is already active");
// this func will only be called once
cp.ready =true;
}
functionwithdrawProtocolFeeBalance(address _to) externalonlyRouter{
if (protocolFeeBalance >0) {
uint256 amountOfLD = amountSDtoLD(protocolFeeBalance);
protocolFeeBalance =0;
_safeTransfer(token, _to, amountOfLD);
emit WithdrawProtocolFeeBalance(_to, amountOfLD);
}
}
functionwithdrawMintFeeBalance(address _to) externalonlyRouter{
if (mintFeeBalance >0) {
uint256 amountOfLD = amountSDtoLD(mintFeeBalance);
mintFeeBalance =0;
_safeTransfer(token, _to, amountOfLD);
emit WithdrawMintFeeBalance(_to, amountOfLD);
}
}
//---------------------------------------------------------------------------// INTERNAL// Conversion Helpers//---------------------------------------------------------------------------functionamountLPtoLD(uint256 _amountLP) externalviewreturns (uint256) {
return amountSDtoLD(_amountLPtoSD(_amountLP));
}
function_amountLPtoSD(uint256 _amountLP) internalviewreturns (uint256) {
require(totalSupply >0, "Stargate: cant convert LPtoSD when totalSupply == 0");
return _amountLP.mul(totalLiquidity).div(totalSupply);
}
function_amountSDtoLP(uint256 _amountSD) internalviewreturns (uint256) {
require(totalLiquidity >0, "Stargate: cant convert SDtoLP when totalLiq == 0");
return _amountSD.mul(totalSupply).div(totalLiquidity);
}
functionamountSDtoLD(uint256 _amount) internalviewreturns (uint256) {
return _amount.mul(convertRate);
}
functionamountLDtoSD(uint256 _amount) internalviewreturns (uint256) {
return _amount.div(convertRate);
}
functiongetAndCheckCP(uint16 _dstChainId, uint256 _dstPoolId) internalviewreturns (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;
}
functiongetChainPath(uint16 _dstChainId, uint256 _dstPoolId) externalviewreturns (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) internalreturns (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);
//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 =newuint256[](cpLength);
uint256 totalDeficit =0;
// algorithm steps 6-9: calculate the total and the amounts required to get to balance statefor (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 distributeduint256 spent;
// handle credits with 2 tranches. the [ < totalDeficit] [excessCredit]// run full Delta, allocate all creditsif (totalDeficit ==0) {
// only fullMode delta will allocate excess creditsif (fullMode && deltaCredit >0) {
// credit ChainPath by weightsfor (uint256 i =0; i < cpLength; ++i) {
ChainPath storage cp = chainPaths[i];
// credits = credits + toBalanceChange + remaining allocation based on weightuint256 amtToCredit = deltaCredit.mul(cp.weight).div(totalWeight);
spent = spent.add(amtToCredit);
cp.credits = cp.credits.add(amtToCredit);
}
} // else do nth
} elseif (totalDeficit <= deltaCredit) {
if (fullMode) {
// algorithm step 13: calculate amount to disperse to bring to balance state or as close as possibleuint256 excessCredit = deltaCredit - totalDeficit;
// algorithm steps 14-16: calculate creditsfor (uint256 i =0; i < cpLength; ++i) {
if (deficit[i] >0) {
ChainPath storage cp = chainPaths[i];
// credits = credits + deficit + remaining allocation based on weightuint256 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 deltaCreditfor (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 deltaCreditfor (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
) internalreturns (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);
// add to credits and call delta. short circuit to save gasif (!batched || deltaCredit > totalLiquidity.mul(lpDeltaBP).div(BP_DENOMINATOR)) {
_delta(defaultLPMode);
}
}
function_safeTransfer(address _token,
address _to,
uint256 _value
) private{
(bool success, bytesmemory data) = _token.call(abi.encodeWithSelector(SELECTOR, _to, _value));
require(success && (data.length==0||abi.decode(data, (bool))), "Stargate: TRANSFER_FAILED");
}
}
Contract Source Code
File 14 of 16: ReentrancyGuard.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.7.0;/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/abstractcontractReentrancyGuard{
// Booleans are more expensive than uint256 or any type that takes up a full// word because each write operation emits an extra SLOAD to first read the// slot's contents, replace the bits taken up by the boolean, and then write// back. This is the compiler's defense against contract upgrades and// pointer aliasing, and it cannot be disabled.// The values being non-zero value makes deployment a bit more expensive,// but in exchange the refund on every call to nonReentrant will be lower in// amount. Since refunds are capped to a percentage of the total// transaction's gas, it is best to keep them low in cases like this one, to// increase the likelihood of the full refund coming into effect.uint256privateconstant _NOT_ENTERED =1;
uint256privateconstant _ENTERED =2;
uint256private _status;
constructor () {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and make it call a
* `private` function that does the actual work.
*/modifiernonReentrant() {
// On the first call to nonReentrant, _notEntered will be truerequire(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
_;
// By storing the original value once again, a refund is triggered (see// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
}
Contract Source Code
File 15 of 16: Router.sol
// SPDX-License-Identifier: BUSL-1.1pragmasolidity 0.7.6;pragmaabicoderv2;// importsimport"@openzeppelin/contracts/access/Ownable.sol";
import"@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import"./Factory.sol";
import"./Pool.sol";
import"./Bridge.sol";
// interfacesimport"@openzeppelin/contracts/token/ERC20/IERC20.sol";
import"./interfaces/IStargateRouter.sol";
import"./interfaces/IStargateReceiver.sol";
// librariesimport"@openzeppelin/contracts/math/SafeMath.sol";
contractRouterisIStargateRouter, Ownable, ReentrancyGuard{
usingSafeMathforuint256;
//---------------------------------------------------------------------------// CONSTANTSuint8internalconstant TYPE_REDEEM_LOCAL_RESPONSE =1;
uint8internalconstant TYPE_REDEEM_LOCAL_CALLBACK_RETRY =2;
uint8internalconstant TYPE_SWAP_REMOTE_RETRY =3;
//---------------------------------------------------------------------------// STRUCTSstructCachedSwap {
address token;
uint256 amountLD;
address to;
bytes payload;
}
//---------------------------------------------------------------------------// VARIABLES
Factory public factory; // used for creating poolsaddresspublic protocolFeeOwner; // can call methods to pull Stargate fees collected in poolsaddresspublic mintFeeOwner; // can call methods to pull mint fees collected in pools
Bridge public bridge;
mapping(uint16=>mapping(bytes=>mapping(uint256=>bytes))) public revertLookup; //[chainId][srcAddress][nonce]mapping(uint16=>mapping(bytes=>mapping(uint256=> CachedSwap))) public cachedSwapLookup; //[chainId][srcAddress][nonce]//---------------------------------------------------------------------------// EVENTSeventRevert(uint8 bridgeFunctionType, uint16 chainId, bytes srcAddress, uint256 nonce);
eventCachedSwapSaved(uint16 chainId, bytes srcAddress, uint256 nonce, address token, uint256 amountLD, address to, bytes payload, bytes reason);
eventRevertRedeemLocal(uint16 srcChainId, uint256 _srcPoolId, uint256 _dstPoolId, bytes to, uint256 redeemAmountSD, uint256 mintAmountSD, uint256indexed nonce, bytesindexed srcAddress);
eventRedeemLocalCallback(uint16 srcChainId, bytesindexed srcAddress, uint256indexed nonce, uint256 srcPoolId, uint256 dstPoolId, address to, uint256 amountSD, uint256 mintAmountSD);
//---------------------------------------------------------------------------// MODIFIERSmodifieronlyBridge() {
require(msg.sender==address(bridge), "Bridge: caller must be Bridge.");
_;
}
constructor() {}
functionsetBridgeAndFactory(Bridge _bridge, Factory _factory) externalonlyOwner{
require(address(bridge) ==address(0x0) &&address(factory) ==address(0x0), "Stargate: bridge and factory already initialized"); // 1 time onlyrequire(address(_bridge) !=address(0x0), "Stargate: bridge cant be 0x0");
require(address(_factory) !=address(0x0), "Stargate: factory cant be 0x0");
bridge = _bridge;
factory = _factory;
}
//---------------------------------------------------------------------------// VIEWSfunction_getPool(uint256 _poolId) internalviewreturns (Pool pool) {
pool = factory.getPool(_poolId);
require(address(pool) !=address(0x0), "Stargate: Pool does not exist");
}
//---------------------------------------------------------------------------// INTERNALfunction_safeTransferFrom(address token,
addressfrom,
address to,
uint256 value
) private{
// bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
(bool success, bytesmemory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
require(success && (data.length==0||abi.decode(data, (bool))), "Stargate: TRANSFER_FROM_FAILED");
}
//---------------------------------------------------------------------------// LOCAL CHAIN FUNCTIONSfunctionaddLiquidity(uint256 _poolId,
uint256 _amountLD,
address _to
) externaloverridenonReentrant{
Pool pool = _getPool(_poolId);
uint256 convertRate = pool.convertRate();
_amountLD = _amountLD.div(convertRate).mul(convertRate);
_safeTransferFrom(pool.token(), msg.sender, address(pool), _amountLD);
pool.mint(_to, _amountLD);
}
functionswap(uint16 _dstChainId,
uint256 _srcPoolId,
uint256 _dstPoolId,
addresspayable _refundAddress,
uint256 _amountLD,
uint256 _minAmountLD,
lzTxObj memory _lzTxParams,
bytescalldata _to,
bytescalldata _payload
) externalpayableoverridenonReentrant{
require(_amountLD >0, "Stargate: cannot swap 0");
require(_refundAddress !=address(0x0), "Stargate: _refundAddress cannot be 0x0");
Pool.SwapObj memory s;
Pool.CreditObj memory c;
{
Pool pool = _getPool(_srcPoolId);
{
uint256 convertRate = pool.convertRate();
_amountLD = _amountLD.div(convertRate).mul(convertRate);
}
s = pool.swap(_dstChainId, _dstPoolId, msg.sender, _amountLD, _minAmountLD, true);
_safeTransferFrom(pool.token(), msg.sender, address(pool), _amountLD);
c = pool.sendCredits(_dstChainId, _dstPoolId);
}
bridge.swap{value: msg.value}(_dstChainId, _srcPoolId, _dstPoolId, _refundAddress, c, s, _lzTxParams, _to, _payload);
}
functionredeemRemote(uint16 _dstChainId,
uint256 _srcPoolId,
uint256 _dstPoolId,
addresspayable _refundAddress,
uint256 _amountLP,
uint256 _minAmountLD,
bytescalldata _to,
lzTxObj memory _lzTxParams
) externalpayableoverridenonReentrant{
require(_refundAddress !=address(0x0), "Stargate: _refundAddress cannot be 0x0");
require(_amountLP >0, "Stargate: not enough lp to redeemRemote");
Pool.SwapObj memory s;
Pool.CreditObj memory c;
{
Pool pool = _getPool(_srcPoolId);
uint256 amountLD = pool.amountLPtoLD(_amountLP);
// perform a swap with no liquidity
s = pool.swap(_dstChainId, _dstPoolId, msg.sender, amountLD, _minAmountLD, false);
pool.redeemRemote(_dstChainId, _dstPoolId, msg.sender, _amountLP);
c = pool.sendCredits(_dstChainId, _dstPoolId);
}
// equal to a swap, with no payload ("0x") no dstGasForCall 0
bridge.swap{value: msg.value}(_dstChainId, _srcPoolId, _dstPoolId, _refundAddress, c, s, _lzTxParams, _to, "");
}
functioninstantRedeemLocal(uint16 _srcPoolId,
uint256 _amountLP,
address _to
) externaloverridenonReentrantreturns (uint256 amountSD) {
require(_amountLP >0, "Stargate: not enough lp to redeem");
Pool pool = _getPool(_srcPoolId);
amountSD = pool.instantRedeemLocal(msg.sender, _amountLP, _to);
}
functionredeemLocal(uint16 _dstChainId,
uint256 _srcPoolId,
uint256 _dstPoolId,
addresspayable _refundAddress,
uint256 _amountLP,
bytescalldata _to,
lzTxObj memory _lzTxParams
) externalpayableoverridenonReentrant{
require(_refundAddress !=address(0x0), "Stargate: _refundAddress cannot be 0x0");
Pool pool = _getPool(_srcPoolId);
require(_amountLP >0, "Stargate: not enough lp to redeem");
uint256 amountSD = pool.redeemLocal(msg.sender, _amountLP, _dstChainId, _dstPoolId, _to);
require(amountSD >0, "Stargate: not enough lp to redeem with amountSD");
Pool.CreditObj memory c = pool.sendCredits(_dstChainId, _dstPoolId);
bridge.redeemLocal{value: msg.value}(_dstChainId, _srcPoolId, _dstPoolId, _refundAddress, c, amountSD, _to, _lzTxParams);
}
functionsendCredits(uint16 _dstChainId,
uint256 _srcPoolId,
uint256 _dstPoolId,
addresspayable _refundAddress
) externalpayableoverridenonReentrant{
require(_refundAddress !=address(0x0), "Stargate: _refundAddress cannot be 0x0");
Pool pool = _getPool(_srcPoolId);
Pool.CreditObj memory c = pool.sendCredits(_dstChainId, _dstPoolId);
bridge.sendCredits{value: msg.value}(_dstChainId, _srcPoolId, _dstPoolId, _refundAddress, c);
}
functionquoteLayerZeroFee(uint16 _dstChainId,
uint8 _functionType,
bytescalldata _toAddress,
bytescalldata _transferAndCallPayload,
Router.lzTxObj memory _lzTxParams
) externalviewoverridereturns (uint256, uint256) {
return bridge.quoteLayerZeroFee(_dstChainId, _functionType, _toAddress, _transferAndCallPayload, _lzTxParams);
}
functionrevertRedeemLocal(uint16 _dstChainId,
bytescalldata _srcAddress,
uint256 _nonce,
addresspayable _refundAddress,
lzTxObj memory _lzTxParams
) externalpayable{
require(_refundAddress !=address(0x0), "Stargate: _refundAddress cannot be 0x0");
bytesmemory payload = revertLookup[_dstChainId][_srcAddress][_nonce];
require(payload.length>0, "Stargate: no retry revert");
{
uint8 functionType;
assembly {
functionType :=mload(add(payload, 32))
}
require(functionType == TYPE_REDEEM_LOCAL_RESPONSE, "Stargate: invalid function type");
}
// empty it
revertLookup[_dstChainId][_srcAddress][_nonce] ="";
uint256 srcPoolId;
uint256 dstPoolId;
assembly {
srcPoolId :=mload(add(payload, 64))
dstPoolId :=mload(add(payload, 96))
}
Pool.CreditObj memory c;
{
Pool pool = _getPool(dstPoolId);
c = pool.sendCredits(_dstChainId, srcPoolId);
}
bridge.redeemLocalCallback{value: msg.value}(_dstChainId, _refundAddress, c, _lzTxParams, payload);
}
functionretryRevert(uint16 _srcChainId,
bytescalldata _srcAddress,
uint256 _nonce
) externalpayable{
bytesmemory payload = revertLookup[_srcChainId][_srcAddress][_nonce];
require(payload.length>0, "Stargate: no retry revert");
// empty it
revertLookup[_srcChainId][_srcAddress][_nonce] ="";
uint8 functionType;
assembly {
functionType :=mload(add(payload, 32))
}
if (functionType == TYPE_REDEEM_LOCAL_CALLBACK_RETRY) {
(, uint256 srcPoolId, uint256 dstPoolId, address to, uint256 amountSD, uint256 mintAmountSD) =abi.decode(
payload,
(uint8, uint256, uint256, address, uint256, uint256)
);
_redeemLocalCallback(_srcChainId, _srcAddress, _nonce, srcPoolId, dstPoolId, to, amountSD, mintAmountSD);
}
// for retrying the swapRemote. if it fails again, retryelseif (functionType == TYPE_SWAP_REMOTE_RETRY) {
(, uint256 srcPoolId, uint256 dstPoolId, uint256 dstGasForCall, address to, Pool.SwapObj memory s, bytesmemory p) =abi.decode(
payload,
(uint8, uint256, uint256, uint256, address, Pool.SwapObj, bytes)
);
_swapRemote(_srcChainId, _srcAddress, _nonce, srcPoolId, dstPoolId, dstGasForCall, to, s, p);
} else {
revert("Stargate: invalid function type");
}
}
functionclearCachedSwap(uint16 _srcChainId,
bytescalldata _srcAddress,
uint256 _nonce
) external{
CachedSwap memory cs = cachedSwapLookup[_srcChainId][_srcAddress][_nonce];
require(cs.to !=address(0x0), "Stargate: cache already cleared");
// clear the data
cachedSwapLookup[_srcChainId][_srcAddress][_nonce] = CachedSwap(address(0x0), 0, address(0x0), "");
IStargateReceiver(cs.to).sgReceive(_srcChainId, _srcAddress, _nonce, cs.token, cs.amountLD, cs.payload);
}
functioncreditChainPath(uint16 _dstChainId,
uint256 _dstPoolId,
uint256 _srcPoolId,
Pool.CreditObj memory _c
) externalonlyBridge{
Pool pool = _getPool(_srcPoolId);
pool.creditChainPath(_dstChainId, _dstPoolId, _c);
}
//---------------------------------------------------------------------------// REMOTE CHAIN FUNCTIONSfunctionredeemLocalCheckOnRemote(uint16 _srcChainId,
bytesmemory _srcAddress,
uint256 _nonce,
uint256 _srcPoolId,
uint256 _dstPoolId,
uint256 _amountSD,
bytescalldata _to
) externalonlyBridge{
Pool pool = _getPool(_dstPoolId);
try pool.redeemLocalCheckOnRemote(_srcChainId, _srcPoolId, _amountSD) returns (uint256 redeemAmountSD, uint256 mintAmountSD) {
revertLookup[_srcChainId][_srcAddress][_nonce] =abi.encode(
TYPE_REDEEM_LOCAL_RESPONSE,
_srcPoolId,
_dstPoolId,
redeemAmountSD,
mintAmountSD,
_to
);
emit RevertRedeemLocal(_srcChainId, _srcPoolId, _dstPoolId, _to, redeemAmountSD, mintAmountSD, _nonce, _srcAddress);
} catch {
// if the func fail, return [swapAmount: 0, mintAMount: _amountSD]// swapAmount represents the amount of chainPath balance deducted on the remote side, which because the above tx failed, should be 0// mintAmount is the full amount of tokens the user attempted to redeem on the src side, which gets converted back into the lp amount
revertLookup[_srcChainId][_srcAddress][_nonce] =abi.encode(TYPE_REDEEM_LOCAL_RESPONSE, _srcPoolId, _dstPoolId, 0, _amountSD, _to);
emit Revert(TYPE_REDEEM_LOCAL_RESPONSE, _srcChainId, _srcAddress, _nonce);
}
}
functionredeemLocalCallback(uint16 _srcChainId,
bytesmemory _srcAddress,
uint256 _nonce,
uint256 _srcPoolId,
uint256 _dstPoolId,
address _to,
uint256 _amountSD,
uint256 _mintAmountSD
) externalonlyBridge{
_redeemLocalCallback(_srcChainId, _srcAddress, _nonce, _srcPoolId, _dstPoolId, _to, _amountSD, _mintAmountSD);
}
function_redeemLocalCallback(uint16 _srcChainId,
bytesmemory _srcAddress,
uint256 _nonce,
uint256 _srcPoolId,
uint256 _dstPoolId,
address _to,
uint256 _amountSD,
uint256 _mintAmountSD
) internal{
Pool pool = _getPool(_dstPoolId);
try pool.redeemLocalCallback(_srcChainId, _srcPoolId, _to, _amountSD, _mintAmountSD) {} catch {
revertLookup[_srcChainId][_srcAddress][_nonce] =abi.encode(
TYPE_REDEEM_LOCAL_CALLBACK_RETRY,
_srcPoolId,
_dstPoolId,
_to,
_amountSD,
_mintAmountSD
);
emit Revert(TYPE_REDEEM_LOCAL_CALLBACK_RETRY, _srcChainId, _srcAddress, _nonce);
}
emit RedeemLocalCallback(_srcChainId, _srcAddress, _nonce, _srcPoolId, _dstPoolId, _to, _amountSD, _mintAmountSD);
}
functionswapRemote(uint16 _srcChainId,
bytesmemory _srcAddress,
uint256 _nonce,
uint256 _srcPoolId,
uint256 _dstPoolId,
uint256 _dstGasForCall,
address _to,
Pool.SwapObj memory _s,
bytesmemory _payload
) externalonlyBridge{
_swapRemote(_srcChainId, _srcAddress, _nonce, _srcPoolId, _dstPoolId, _dstGasForCall, _to, _s, _payload);
}
function_swapRemote(uint16 _srcChainId,
bytesmemory _srcAddress,
uint256 _nonce,
uint256 _srcPoolId,
uint256 _dstPoolId,
uint256 _dstGasForCall,
address _to,
Pool.SwapObj memory _s,
bytesmemory _payload
) internal{
Pool pool = _getPool(_dstPoolId);
// first try catch the swap remotetry pool.swapRemote(_srcChainId, _srcPoolId, _to, _s) returns (uint256 amountLD) {
if (_payload.length>0) {
// then try catch the external contract calltry IStargateReceiver(_to).sgReceive{gas: _dstGasForCall}(_srcChainId, _srcAddress, _nonce, pool.token(), amountLD, _payload) {
// do nothing
} catch (bytesmemory reason) {
cachedSwapLookup[_srcChainId][_srcAddress][_nonce] = CachedSwap(pool.token(), amountLD, _to, _payload);
emit CachedSwapSaved(_srcChainId, _srcAddress, _nonce, pool.token(), amountLD, _to, _payload, reason);
}
}
} catch {
revertLookup[_srcChainId][_srcAddress][_nonce] =abi.encode(
TYPE_SWAP_REMOTE_RETRY,
_srcPoolId,
_dstPoolId,
_dstGasForCall,
_to,
_s,
_payload
);
emit Revert(TYPE_SWAP_REMOTE_RETRY, _srcChainId, _srcAddress, _nonce);
}
}
//---------------------------------------------------------------------------// DAO CallsfunctioncreatePool(uint256 _poolId,
address _token,
uint8 _sharedDecimals,
uint8 _localDecimals,
stringmemory _name,
stringmemory _symbol
) externalonlyOwnerreturns (address) {
require(_token !=address(0x0), "Stargate: _token cannot be 0x0");
return factory.createPool(_poolId, _token, _sharedDecimals, _localDecimals, _name, _symbol);
}
functioncreateChainPath(uint256 _poolId,
uint16 _dstChainId,
uint256 _dstPoolId,
uint256 _weight
) externalonlyOwner{
Pool pool = _getPool(_poolId);
pool.createChainPath(_dstChainId, _dstPoolId, _weight);
}
functionactivateChainPath(uint256 _poolId,
uint16 _dstChainId,
uint256 _dstPoolId
) externalonlyOwner{
Pool pool = _getPool(_poolId);
pool.activateChainPath(_dstChainId, _dstPoolId);
}
functionsetWeightForChainPath(uint256 _poolId,
uint16 _dstChainId,
uint256 _dstPoolId,
uint16 _weight
) externalonlyOwner{
Pool pool = _getPool(_poolId);
pool.setWeightForChainPath(_dstChainId, _dstPoolId, _weight);
}
functionsetProtocolFeeOwner(address _owner) externalonlyOwner{
require(_owner !=address(0x0), "Stargate: _owner cannot be 0x0");
protocolFeeOwner = _owner;
}
functionsetMintFeeOwner(address _owner) externalonlyOwner{
require(_owner !=address(0x0), "Stargate: _owner cannot be 0x0");
mintFeeOwner = _owner;
}
functionsetFees(uint256 _poolId, uint256 _mintFeeBP) externalonlyOwner{
Pool pool = _getPool(_poolId);
pool.setFee(_mintFeeBP);
}
functionsetFeeLibrary(uint256 _poolId, address _feeLibraryAddr) externalonlyOwner{
Pool pool = _getPool(_poolId);
pool.setFeeLibrary(_feeLibraryAddr);
}
functionsetSwapStop(uint256 _poolId, bool _swapStop) externalonlyOwner{
Pool pool = _getPool(_poolId);
pool.setSwapStop(_swapStop);
}
functionsetDeltaParam(uint256 _poolId,
bool _batched,
uint256 _swapDeltaBP,
uint256 _lpDeltaBP,
bool _defaultSwapMode,
bool _defaultLPMode
) externalonlyOwner{
Pool pool = _getPool(_poolId);
pool.setDeltaParam(_batched, _swapDeltaBP, _lpDeltaBP, _defaultSwapMode, _defaultLPMode);
}
functioncallDelta(uint256 _poolId, bool _fullMode) external{
Pool pool = _getPool(_poolId);
pool.callDelta(_fullMode);
}
functionwithdrawMintFee(uint256 _poolId, address _to) external{
require(mintFeeOwner ==msg.sender, "Stargate: only mintFeeOwner");
Pool pool = _getPool(_poolId);
pool.withdrawMintFeeBalance(_to);
}
functionwithdrawProtocolFee(uint256 _poolId, address _to) external{
require(protocolFeeOwner ==msg.sender, "Stargate: only protocolFeeOwner");
Pool pool = _getPool(_poolId);
pool.withdrawProtocolFeeBalance(_to);
}
}
Contract Source Code
File 16 of 16: SafeMath.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.7.0;/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/librarySafeMath{
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/functiontryAdd(uint256 a, uint256 b) internalpurereturns (bool, uint256) {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
/**
* @dev Returns the substraction of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/functiontrySub(uint256 a, uint256 b) internalpurereturns (bool, uint256) {
if (b > a) return (false, 0);
return (true, a - b);
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/functiontryMul(uint256 a, uint256 b) internalpurereturns (bool, uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the// benefit is lost if 'b' is also tested.// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522if (a ==0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/functiontryDiv(uint256 a, uint256 b) internalpurereturns (bool, uint256) {
if (b ==0) return (false, 0);
return (true, a / b);
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/functiontryMod(uint256 a, uint256 b) internalpurereturns (bool, uint256) {
if (b ==0) return (false, 0);
return (true, a % b);
}
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
*
* - Addition cannot overflow.
*/functionadd(uint256 a, uint256 b) internalpurereturns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/functionsub(uint256 a, uint256 b) internalpurereturns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
return a - b;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
*
* - Multiplication cannot overflow.
*/functionmul(uint256 a, uint256 b) internalpurereturns (uint256) {
if (a ==0) return0;
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers, reverting on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/functiondiv(uint256 a, uint256 b) internalpurereturns (uint256) {
require(b >0, "SafeMath: division by zero");
return a / b;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/functionmod(uint256 a, uint256 b) internalpurereturns (uint256) {
require(b >0, "SafeMath: modulo by zero");
return a % b;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {trySub}.
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/functionsub(uint256 a, uint256 b, stringmemory errorMessage) internalpurereturns (uint256) {
require(b <= a, errorMessage);
return a - b;
}
/**
* @dev Returns the integer division of two unsigned integers, reverting with custom message on
* division by zero. The result is rounded towards zero.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryDiv}.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/functiondiv(uint256 a, uint256 b, stringmemory errorMessage) internalpurereturns (uint256) {
require(b >0, errorMessage);
return a / b;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting with custom message when dividing by zero.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryMod}.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/functionmod(uint256 a, uint256 b, stringmemory errorMessage) internalpurereturns (uint256) {
require(b >0, errorMessage);
return a % b;
}
}