@etherspot/contracts
Version:
Etherspot Solidity contracts
38 lines • 91.5 kB
JSON
{
"language": "Solidity",
"sources": {
"src/bridges/facets/StargateFacet.sol": {
"content": "// SPDX-License-Identifier: MIT\r\npragma solidity 0.8.4;\r\n\r\nimport {IStargateRouter} from \"../interfaces/IStargateRouter.sol\";\r\nimport {IStargateReceiver} from \"../interfaces/IStargateReceiver.sol\";\r\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\r\nimport {SafeERC20} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\r\nimport {ReentrancyGuard} from \"../../common/helpers/DiamondReentrancyGuard.sol\";\r\nimport {CannotBridgeToSameNetwork, InvalidAmount, InvalidConfig} from \"../errors/GenericErrors.sol\";\r\nimport {SenderNotStargateRouter, NoMsgValueForCrossChainMessage, StargateRouterAddressZero, InvalidSourcePoolId, InvalidDestinationPoolId} from \"../errors/StargateErrors.sol\";\r\nimport {LibDiamond} from \"../libs/LibDiamond.sol\";\r\n\r\n/// @title StargateFacet\r\n/// @author Luke Wickens <luke@pillarproject.io>\r\n/// @notice Stargate/LayerZero intergration for bridging tokens\r\n\r\ncontract StargateFacet is IStargateReceiver, ReentrancyGuard {\r\n using SafeERC20 for IERC20;\r\n\r\n //////////////////////////////////////////////////////////////\r\n /////////////////////////// Events ///////////////////////////\r\n //////////////////////////////////////////////////////////////\r\n event SGInitialized(address stargate, uint16 chainId);\r\n event SGTransferStarted(\r\n string bridgeUsed,\r\n address fromToken,\r\n address toToken,\r\n address from,\r\n address to,\r\n uint256 amount,\r\n uint16 chainIdTo\r\n );\r\n event SGReceivedOnDestination(address token, uint256 amount);\r\n event SGUpdatedRouter(address newAddress);\r\n event SGUpdatedSlippageTolerance(uint256 newSlippage);\r\n event SGAddedPool(uint16 chainId, address token, uint16 poolId);\r\n\r\n //////////////////////////////////////////////////////////////\r\n ////////////////////////// Storage ///////////////////////////\r\n //////////////////////////////////////////////////////////////\r\n\r\n bytes32 internal constant NAMESPACE =\r\n keccak256(\"io.etherspot.facets.stargate\");\r\n struct Storage {\r\n address stargateRouter;\r\n uint16 chainId;\r\n uint256 dstGas;\r\n uint256 slippage;\r\n mapping(uint16 => mapping(address => uint16)) poolIds;\r\n }\r\n\r\n //////////////////////////////////////////////////////////////\r\n ////////////////////////// Structs ///////////////////////////\r\n //////////////////////////////////////////////////////////////\r\n\r\n struct StargateData {\r\n uint256 qty;\r\n address fromToken;\r\n address toToken;\r\n uint16 dstChainId;\r\n address to;\r\n address destStargateComposed;\r\n }\r\n\r\n /// @notice initializes state variables for the Stargate facet\r\n /// @param _stargateRouter - address of the Stargate router contract\r\n /// @param _chainId - current chain id\r\n function sgInitialize(address _stargateRouter, uint16 _chainId) external {\r\n if (_stargateRouter == address(0)) revert InvalidConfig();\r\n LibDiamond.enforceIsContractOwner();\r\n Storage storage s = getStorage();\r\n s.stargateRouter = address(_stargateRouter);\r\n s.chainId = _chainId;\r\n s.slippage = 50; // equates to 0.5%\r\n // Adding pre-existing pools => USDC: 1, USDT: 2, BUSD: 5\r\n sgAddPool(1, 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48, 1);\r\n sgAddPool(1, 0xdAC17F958D2ee523a2206206994597C13D831ec7, 2);\r\n sgAddPool(2, 0x55d398326f99059fF775485246999027B3197955, 2);\r\n sgAddPool(2, 0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56, 5);\r\n sgAddPool(6, 0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E, 1);\r\n sgAddPool(6, 0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7, 2);\r\n sgAddPool(9, 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174, 1);\r\n sgAddPool(9, 0xc2132D05D31c914a87C6611C10748AEb04B58e8F, 2);\r\n sgAddPool(10, 0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8, 1);\r\n sgAddPool(10, 0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9, 2);\r\n sgAddPool(11, 0x7F5c764cBc14f9669B88837ca1490cCa17c31607, 1);\r\n sgAddPool(12, 0x04068DA6C83AFCFA0e13ba15A6696662335D5B75, 1);\r\n emit SGInitialized(_stargateRouter, _chainId);\r\n }\r\n\r\n /// @notice initializes state variables for the stargate facet\r\n /// @param _sgData - struct containing information required to execute bridge\r\n function sgBridgeTokens(StargateData memory _sgData)\r\n external\r\n payable\r\n nonReentrant\r\n {\r\n // if (msg.value <= 0) revert NoMsgValueForCrossChainMessage();\r\n if (_sgData.qty <= 0) revert InvalidAmount();\r\n if (\r\n _sgData.fromToken == address(0) ||\r\n _sgData.toToken == address(0) ||\r\n _sgData.to == address(0) ||\r\n _sgData.destStargateComposed == address(0)\r\n ) revert InvalidConfig();\r\n\r\n // access storage\r\n Storage storage s = getStorage();\r\n\r\n // check pool ids are valid\r\n uint16 srcPoolId = sgRetrievePoolId(s.chainId, _sgData.fromToken);\r\n if (srcPoolId == 0) revert InvalidSourcePoolId();\r\n uint16 dstPoolId = sgRetrievePoolId(\r\n _sgData.dstChainId,\r\n _sgData.toToken\r\n );\r\n\r\n // calculate cross chain fees\r\n uint256 fees = sgCalculateFees(\r\n _sgData.dstChainId,\r\n _sgData.to,\r\n s.stargateRouter\r\n );\r\n\r\n // calculate slippage\r\n uint256 minAmountOut = sgMinAmountOut(_sgData.qty);\r\n\r\n // encode sgReceive implemented\r\n bytes memory destination = abi.encodePacked(\r\n _sgData.destStargateComposed\r\n );\r\n\r\n // encode payload data to send to destination contract, which it will handle with sgReceive()\r\n bytes memory payload = abi.encode(_sgData.to);\r\n\r\n // this contract calls stargate swap()\r\n IERC20(_sgData.fromToken).safeTransferFrom(\r\n msg.sender,\r\n address(this),\r\n _sgData.qty\r\n );\r\n\r\n IERC20(_sgData.fromToken).safeApprove(\r\n address(s.stargateRouter),\r\n _sgData.qty\r\n );\r\n\r\n // Stargate's Router.swap() function sends the tokens to the destination chain.\r\n IStargateRouter(s.stargateRouter).swap{value: fees}(\r\n _sgData.dstChainId, // the destination chain id\r\n srcPoolId, // the source Stargate poolId\r\n dstPoolId, // the destination Stargate poolId\r\n payable(msg.sender), // refund adddress. if msg.sender pays too much gas, return extra eth\r\n _sgData.qty, // total tokens to send to destination chain\r\n minAmountOut, // min amount allowed out\r\n IStargateRouter.lzTxObj(200000, 0, \"0x\"), // default lzTxObj\r\n destination, // destination address, the sgReceive() implementer\r\n payload // bytes payload\r\n );\r\n\r\n emit SGTransferStarted(\r\n \"stargate\",\r\n _sgData.fromToken,\r\n _sgData.toToken,\r\n msg.sender,\r\n _sgData.to,\r\n _sgData.qty,\r\n _sgData.dstChainId\r\n );\r\n }\r\n\r\n /// @notice required to receive tokens on destination chain\r\n /// @param _chainId The remote chainId sending the tokens\r\n /// @param _srcAddress The remote Bridge address\r\n /// @param _nonce The message ordering nonce\r\n /// @param _token The token contract on the local chain\r\n /// @param amountLD The qty of local _token contract tokens\r\n /// @param _payload The bytes containing the toAddress\r\n function sgReceive(\r\n uint16 _chainId,\r\n bytes memory _srcAddress,\r\n uint256 _nonce,\r\n address _token,\r\n uint256 amountLD,\r\n bytes memory _payload\r\n ) external override {\r\n Storage storage s = getStorage();\r\n if (msg.sender != address(s.stargateRouter))\r\n revert SenderNotStargateRouter();\r\n\r\n address _toAddr = abi.decode(_payload, (address));\r\n IERC20(_token).transfer(_toAddr, amountLD);\r\n emit SGReceivedOnDestination(_token, amountLD);\r\n }\r\n\r\n /// @notice Calculates cross chain fee\r\n /// @param _destChain Destination chain id\r\n /// @param _receiver Receiver on destination chain\r\n /// @param _router Address of stargate router\r\n function sgCalculateFees(\r\n uint16 _destChain,\r\n address _receiver,\r\n address _router\r\n ) public view returns (uint256) {\r\n (uint256 nativeFee, ) = IStargateRouter(_router).quoteLayerZeroFee(\r\n _destChain, // destination chain id\r\n 1, // 1 = swap\r\n abi.encodePacked(_receiver), // receiver on destination chain\r\n \"0x\", // payload, using abi.encode()\r\n IStargateRouter.lzTxObj(200000, 0, \"0x\")\r\n );\r\n return nativeFee;\r\n }\r\n\r\n /// @notice Calculates the minimum amount out using slippage tolerance\r\n /// @param _amount Transfer amount\r\n function sgMinAmountOut(uint256 _amount) public view returns (uint256) {\r\n Storage storage s = getStorage();\r\n // equates to 0.5% slippage\r\n return (_amount * (10000 - s.slippage)) / (10000);\r\n }\r\n\r\n /// @notice Updates stargate router address for deployed chain\r\n /// @param _newAddress Address of the new router\r\n function sgUpdateRouter(address _newAddress) external {\r\n LibDiamond.enforceIsContractOwner();\r\n if (_newAddress == address(0)) revert StargateRouterAddressZero();\r\n Storage storage s = getStorage();\r\n s.stargateRouter = address(_newAddress);\r\n emit SGUpdatedRouter(_newAddress);\r\n }\r\n\r\n /// @notice Updates slippage tolerance amount\r\n /// @param _newSlippage New slippage amount\r\n function sgUpdateSlippageTolerance(uint256 _newSlippage) external {\r\n LibDiamond.enforceIsContractOwner();\r\n Storage storage s = getStorage();\r\n s.slippage = _newSlippage;\r\n emit SGUpdatedSlippageTolerance(_newSlippage);\r\n }\r\n\r\n /// @notice Adds a new pool for a specific token and chain\r\n /// @param _chainId Chain id of new pool (NOT actual chain id - check stargate pool ids docs)\r\n /// @param _token Address of token\r\n /// @param _poolId Pool id (check stargate pool ids docs)\r\n function sgAddPool(\r\n uint16 _chainId,\r\n address _token,\r\n uint16 _poolId\r\n ) public {\r\n LibDiamond.enforceIsContractOwner();\r\n Storage storage s = getStorage();\r\n s.poolIds[_chainId][_token] = _poolId;\r\n emit SGAddedPool(_chainId, _token, _poolId);\r\n }\r\n\r\n /// @notice Checks for a valid token pool on specific chain\r\n /// @param _chainId Chain id of new pool (NOT actual chain id - check stargate pool ids docs)\r\n /// @param _token Address of token\r\n /// @param _poolId Pool id (check stargate pool ids docs)\r\n function sgCheckPoolId(\r\n uint16 _chainId,\r\n address _token,\r\n uint16 _poolId\r\n ) external view returns (bool) {\r\n Storage storage s = getStorage();\r\n return s.poolIds[_chainId][_token] == _poolId ? true : false;\r\n }\r\n\r\n /// @notice Retrieves pool id for a token on a specified chain\r\n /// @param _chainId Chain id of new pool (NOT actual chain id - check stargate pool ids docs)\r\n /// @param _token Address of token\r\n function sgRetrievePoolId(uint16 _chainId, address _token)\r\n public\r\n view\r\n returns (uint16)\r\n {\r\n Storage storage s = getStorage();\r\n return s.poolIds[_chainId][_token];\r\n }\r\n\r\n //////////////////////////////////////////////////////////////\r\n ////////////////////// Private Functions /////////////////////\r\n //////////////////////////////////////////////////////////////\r\n\r\n /// @dev fetch local storage\r\n function getStorage() private pure returns (Storage storage s) {\r\n bytes32 namespace = NAMESPACE;\r\n // solhint-disable-next-line no-inline-assembly\r\n assembly {\r\n s.slot := namespace\r\n }\r\n }\r\n}\r\n"
},
"src/bridges/interfaces/IStargateRouter.sol": {
"content": "// SPDX-License-Identifier:MIT\r\n\r\npragma solidity 0.8.4;\r\npragma abicoder v2;\r\n\r\ninterface IStargateRouter {\r\n struct lzTxObj {\r\n uint256 dstGasForCall;\r\n uint256 dstNativeAmount;\r\n bytes dstNativeAddr;\r\n }\r\n\r\n function addLiquidity(\r\n uint256 _poolId,\r\n uint256 _amountLD,\r\n address _to\r\n ) external;\r\n\r\n function swap(\r\n uint16 _dstChainId,\r\n uint256 _srcPoolId,\r\n uint256 _dstPoolId,\r\n address payable _refundAddress,\r\n uint256 _amountLD,\r\n uint256 _minAmountLD,\r\n lzTxObj memory _lzTxParams,\r\n bytes calldata _to,\r\n bytes calldata _payload\r\n ) external payable;\r\n\r\n function redeemRemote(\r\n uint16 _dstChainId,\r\n uint256 _srcPoolId,\r\n uint256 _dstPoolId,\r\n address payable _refundAddress,\r\n uint256 _amountLP,\r\n uint256 _minAmountLD,\r\n bytes calldata _to,\r\n lzTxObj memory _lzTxParams\r\n ) external payable;\r\n\r\n function instantRedeemLocal(\r\n uint16 _srcPoolId,\r\n uint256 _amountLP,\r\n address _to\r\n ) external returns (uint256);\r\n\r\n function redeemLocal(\r\n uint16 _dstChainId,\r\n uint256 _srcPoolId,\r\n uint256 _dstPoolId,\r\n address payable _refundAddress,\r\n uint256 _amountLP,\r\n bytes calldata _to,\r\n lzTxObj memory _lzTxParams\r\n ) external payable;\r\n\r\n function sendCredits(\r\n uint16 _dstChainId,\r\n uint256 _srcPoolId,\r\n uint256 _dstPoolId,\r\n address payable _refundAddress\r\n ) external payable;\r\n\r\n function quoteLayerZeroFee(\r\n uint16 _dstChainId,\r\n uint8 _functionType,\r\n bytes calldata _toAddress,\r\n bytes calldata _transferAndCallPayload,\r\n lzTxObj memory _lzTxParams\r\n ) external view returns (uint256, uint256);\r\n}\r\n"
},
"src/bridges/interfaces/IStargateReceiver.sol": {
"content": "// SPDX-License-Identifier: MIT\r\n\r\npragma solidity 0.8.4;\r\n\r\ninterface IStargateReceiver {\r\n function sgReceive(\r\n uint16 _srcChainId, // the remote chainId sending the tokens\r\n bytes memory _srcAddress, // the remote Bridge address\r\n uint256 _nonce,\r\n address _token, // the token contract on the local chain\r\n uint256 amountLD, // the qty of local _token contract tokens\r\n bytes memory payload\r\n ) external;\r\n}\r\n"
},
"@openzeppelin/contracts/token/ERC20/IERC20.sol": {
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n}\n"
},
"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": {
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n"
},
"src/common/helpers/DiamondReentrancyGuard.sol": {
"content": "// SPDX-License-Identifier: UNLICENSED\r\npragma solidity 0.8.4;\r\n\r\n/// @title Reentrancy Guard\r\n/// @notice Abstract contract to provide protection against reentrancy\r\nabstract contract ReentrancyGuard {\r\n //////////////////////////////////////////////////////////////\r\n ////////////////////////// Storage ///////////////////////////\r\n //////////////////////////////////////////////////////////////\r\n\r\n bytes32 private constant NAMESPACE =\r\n keccak256(\"io.etherspot.helpers.reentrancyguard\");\r\n\r\n //////////////////////////////////////////////////////////////\r\n ////////////////////////// Structs ///////////////////////////\r\n //////////////////////////////////////////////////////////////\r\n\r\n struct ReentrancyStorage {\r\n uint256 status;\r\n }\r\n\r\n //////////////////////////////////////////////////////////////\r\n ////////////////////////// Errors ////////////////////////////\r\n //////////////////////////////////////////////////////////////\r\n\r\n error ReentrancyError();\r\n\r\n //////////////////////////////////////////////////////////////\r\n ///////////////////////// Constants //////////////////////////\r\n //////////////////////////////////////////////////////////////\r\n\r\n uint256 private constant _NOT_ENTERED = 0;\r\n uint256 private constant _ENTERED = 1;\r\n\r\n //////////////////////////////////////////////////////////////\r\n ///////////////////////// Modifiers ///////////////////////////\r\n //////////////////////////////////////////////////////////////\r\n\r\n modifier nonReentrant() {\r\n ReentrancyStorage storage s = reentrancyStorage();\r\n if (s.status == _ENTERED) revert ReentrancyError();\r\n s.status = _ENTERED;\r\n _;\r\n s.status = _NOT_ENTERED;\r\n }\r\n\r\n //////////////////////////////////////////////////////////////\r\n ////////////////////// Private Functions /////////////////////\r\n //////////////////////////////////////////////////////////////\r\n\r\n /// @dev fetch local storage\r\n function reentrancyStorage()\r\n private\r\n pure\r\n returns (ReentrancyStorage storage data)\r\n {\r\n bytes32 position = NAMESPACE;\r\n // solhint-disable-next-line no-inline-assembly\r\n assembly {\r\n data.slot := position\r\n }\r\n }\r\n}\r\n"
},
"src/bridges/errors/GenericErrors.sol": {
"content": "// SPDX-License-Identifier: MIT\r\n// solhint-disable-next-line\r\npragma solidity 0.8.4;\r\n\r\nerror InvalidAmount();\r\nerror TokenAddressIsZero();\r\nerror CannotBridgeToSameNetwork();\r\nerror ZeroPostSwapBalance();\r\nerror InvalidBridgeConfigLength();\r\nerror NoSwapDataProvided();\r\nerror NativeValueWithERC();\r\nerror ContractCallNotAllowed();\r\nerror NullAddrIsNotAValidSpender();\r\nerror NullAddrIsNotAnERC20Token();\r\nerror NoTransferToNullAddress();\r\nerror NativeAssetTransferFailed();\r\nerror InvalidContract();\r\nerror InvalidConfig();\r\nerror ZeroAddressProvided();\r\n"
},
"src/bridges/errors/StargateErrors.sol": {
"content": "// SPDX-License-Identifier: MIT\r\n// solhint-disable-next-line\r\npragma solidity 0.8.4;\r\n\r\nerror SenderNotStargateRouter();\r\nerror NoMsgValueForCrossChainMessage();\r\nerror StargateRouterAddressZero();\r\nerror InvalidSourcePoolId();\r\nerror InvalidDestinationPoolId();\r\n"
},
"src/bridges/libs/LibDiamond.sol": {
"content": "// SPDX-License-Identifier: MIT\r\npragma solidity >=0.8.4 <0.9.0;\r\n\r\nimport {IDiamondCut} from \"../interfaces/IDiamondCut.sol\";\r\n\r\nlibrary LibDiamond {\r\n bytes32 internal constant DIAMOND_STORAGE_POSITION =\r\n keccak256(\"diamond.standard.diamond.storage\");\r\n\r\n struct FacetAddressAndPosition {\r\n address facetAddress;\r\n uint96 functionSelectorPosition; // position in facetFunctionSelectors.functionSelectors array\r\n }\r\n\r\n struct FacetFunctionSelectors {\r\n bytes4[] functionSelectors;\r\n uint256 facetAddressPosition; // position of facetAddress in facetAddresses array\r\n }\r\n\r\n struct DiamondStorage {\r\n // maps function selector to the facet address and\r\n // the position of the selector in the facetFunctionSelectors.selectors array\r\n mapping(bytes4 => FacetAddressAndPosition) selectorToFacetAndPosition;\r\n // maps facet addresses to function selectors\r\n mapping(address => FacetFunctionSelectors) facetFunctionSelectors;\r\n // facet addresses\r\n address[] facetAddresses;\r\n // Used to query if a contract implements an interface.\r\n // Used to implement ERC-165.\r\n mapping(bytes4 => bool) supportedInterfaces;\r\n // owner of the contract\r\n address contractOwner;\r\n }\r\n\r\n function diamondStorage()\r\n internal\r\n pure\r\n returns (DiamondStorage storage ds)\r\n {\r\n bytes32 position = DIAMOND_STORAGE_POSITION;\r\n // solhint-disable-next-line no-inline-assembly\r\n assembly {\r\n ds.slot := position\r\n }\r\n }\r\n\r\n event OwnershipTransferred(\r\n address indexed previousOwner,\r\n address indexed newOwner\r\n );\r\n\r\n function setContractOwner(address _newOwner) internal {\r\n DiamondStorage storage ds = diamondStorage();\r\n address previousOwner = ds.contractOwner;\r\n ds.contractOwner = _newOwner;\r\n emit OwnershipTransferred(previousOwner, _newOwner);\r\n }\r\n\r\n function contractOwner() internal view returns (address contractOwner_) {\r\n contractOwner_ = diamondStorage().contractOwner;\r\n }\r\n\r\n function enforceIsContractOwner() internal view {\r\n require(\r\n msg.sender == diamondStorage().contractOwner,\r\n \"LibDiamond: Must be contract owner\"\r\n );\r\n }\r\n\r\n event DiamondCut(\r\n IDiamondCut.FacetCut[] _diamondCut,\r\n address _init,\r\n bytes _calldata\r\n );\r\n\r\n // Internal function version of diamondCut\r\n function diamondCut(\r\n IDiamondCut.FacetCut[] memory _diamondCut,\r\n address _init,\r\n bytes memory _calldata\r\n ) internal {\r\n for (\r\n uint256 facetIndex;\r\n facetIndex < _diamondCut.length;\r\n facetIndex++\r\n ) {\r\n IDiamondCut.FacetCutAction action = _diamondCut[facetIndex].action;\r\n if (action == IDiamondCut.FacetCutAction.Add) {\r\n addFunctions(\r\n _diamondCut[facetIndex].facetAddress,\r\n _diamondCut[facetIndex].functionSelectors\r\n );\r\n } else if (action == IDiamondCut.FacetCutAction.Replace) {\r\n replaceFunctions(\r\n _diamondCut[facetIndex].facetAddress,\r\n _diamondCut[facetIndex].functionSelectors\r\n );\r\n } else if (action == IDiamondCut.FacetCutAction.Remove) {\r\n removeFunctions(\r\n _diamondCut[facetIndex].facetAddress,\r\n _diamondCut[facetIndex].functionSelectors\r\n );\r\n } else {\r\n revert(\"LibDiamondCut: Incorrect FacetCutAction\");\r\n }\r\n }\r\n emit DiamondCut(_diamondCut, _init, _calldata);\r\n initializeDiamondCut(_init, _calldata);\r\n }\r\n\r\n function addFunctions(\r\n address _facetAddress,\r\n bytes4[] memory _functionSelectors\r\n ) internal {\r\n require(\r\n _functionSelectors.length > 0,\r\n \"LibDiamondCut: No selectors in facet to cut\"\r\n );\r\n DiamondStorage storage ds = diamondStorage();\r\n require(\r\n _facetAddress != address(0),\r\n \"LibDiamondCut: Add facet can't be address(0)\"\r\n );\r\n uint96 selectorPosition = uint96(\r\n ds.facetFunctionSelectors[_facetAddress].functionSelectors.length\r\n );\r\n // add new facet address if it does not exist\r\n if (selectorPosition == 0) {\r\n addFacet(ds, _facetAddress);\r\n }\r\n for (\r\n uint256 selectorIndex;\r\n selectorIndex < _functionSelectors.length;\r\n selectorIndex++\r\n ) {\r\n bytes4 selector = _functionSelectors[selectorIndex];\r\n address oldFacetAddress = ds\r\n .selectorToFacetAndPosition[selector]\r\n .facetAddress;\r\n require(\r\n oldFacetAddress == address(0),\r\n \"LibDiamondCut: Can't add function that already exists\"\r\n );\r\n addFunction(ds, selector, selectorPosition, _facetAddress);\r\n selectorPosition++;\r\n }\r\n }\r\n\r\n function replaceFunctions(\r\n address _facetAddress,\r\n bytes4[] memory _functionSelectors\r\n ) internal {\r\n require(\r\n _functionSelectors.length > 0,\r\n \"LibDiamondCut: No selectors in facet to cut\"\r\n );\r\n DiamondStorage storage ds = diamondStorage();\r\n require(\r\n _facetAddress != address(0),\r\n \"LibDiamondCut: Add facet can't be address(0)\"\r\n );\r\n uint96 selectorPosition = uint96(\r\n ds.facetFunctionSelectors[_facetAddress].functionSelectors.length\r\n );\r\n // add new facet address if it does not exist\r\n if (selectorPosition == 0) {\r\n addFacet(ds, _facetAddress);\r\n }\r\n for (\r\n uint256 selectorIndex;\r\n selectorIndex < _functionSelectors.length;\r\n selectorIndex++\r\n ) {\r\n bytes4 selector = _functionSelectors[selectorIndex];\r\n address oldFacetAddress = ds\r\n .selectorToFacetAndPosition[selector]\r\n .facetAddress;\r\n require(\r\n oldFacetAddress != _facetAddress,\r\n \"LibDiamondCut: Can't replace function with same function\"\r\n );\r\n removeFunction(ds, oldFacetAddress, selector);\r\n addFunction(ds, selector, selectorPosition, _facetAddress);\r\n selectorPosition++;\r\n }\r\n }\r\n\r\n function removeFunctions(\r\n address _facetAddress,\r\n bytes4[] memory _functionSelectors\r\n ) internal {\r\n require(\r\n _functionSelectors.length > 0,\r\n \"LibDiamondCut: No selectors in facet to cut\"\r\n );\r\n DiamondStorage storage ds = diamondStorage();\r\n // if function does not exist then do nothing and return\r\n require(\r\n _facetAddress == address(0),\r\n \"LibDiamondCut: Remove facet address must be address(0)\"\r\n );\r\n for (\r\n uint256 selectorIndex;\r\n selectorIndex < _functionSelectors.length;\r\n selectorIndex++\r\n ) {\r\n bytes4 selector = _functionSelectors[selectorIndex];\r\n address oldFacetAddress = ds\r\n .selectorToFacetAndPosition[selector]\r\n .facetAddress;\r\n removeFunction(ds, oldFacetAddress, selector);\r\n }\r\n }\r\n\r\n function addFacet(DiamondStorage storage ds, address _facetAddress)\r\n internal\r\n {\r\n enforceHasContractCode(\r\n _facetAddress,\r\n \"LibDiamondCut: New facet has no code\"\r\n );\r\n ds.facetFunctionSelectors[_facetAddress].facetAddressPosition = ds\r\n .facetAddresses\r\n .length;\r\n ds.facetAddresses.push(_facetAddress);\r\n }\r\n\r\n function addFunction(\r\n DiamondStorage storage ds,\r\n bytes4 _selector,\r\n uint96 _selectorPosition,\r\n address _facetAddress\r\n ) internal {\r\n ds\r\n .selectorToFacetAndPosition[_selector]\r\n .functionSelectorPosition = _selectorPosition;\r\n ds.facetFunctionSelectors[_facetAddress].functionSelectors.push(\r\n _selector\r\n );\r\n ds.selectorToFacetAndPosition[_selector].facetAddress = _facetAddress;\r\n }\r\n\r\n function removeFunction(\r\n DiamondStorage storage ds,\r\n address _facetAddress,\r\n bytes4 _selector\r\n ) internal {\r\n require(\r\n _facetAddress != address(0),\r\n \"LibDiamondCut: Can't remove function that doesn't exist\"\r\n );\r\n // an immutable function is a function defined directly in a diamond\r\n require(\r\n _facetAddress != address(this),\r\n \"LibDiamondCut: Can't remove immutable function\"\r\n );\r\n // replace selector with last selector, then delete last selector\r\n uint256 selectorPosition = ds\r\n .selectorToFacetAndPosition[_selector]\r\n .functionSelectorPosition;\r\n uint256 lastSelectorPosition = ds\r\n .facetFunctionSelectors[_facetAddress]\r\n .functionSelectors\r\n .length - 1;\r\n // if not the same then replace _selector with lastSelector\r\n if (selectorPosition != lastSelectorPosition) {\r\n bytes4 lastSelector = ds\r\n .facetFunctionSelectors[_facetAddress]\r\n .functionSelectors[lastSelectorPosition];\r\n ds.facetFunctionSelectors[_facetAddress].functionSelectors[\r\n selectorPosition\r\n ] = lastSelector;\r\n ds\r\n .selectorToFacetAndPosition[lastSelector]\r\n .functionSelectorPosition = uint96(selectorPosition);\r\n }\r\n // delete the last selector\r\n ds.facetFunctionSelectors[_facetAddress].functionSelectors.pop();\r\n delete ds.selectorToFacetAndPosition[_selector];\r\n\r\n // if no more selectors for facet address then delete the facet address\r\n if (lastSelectorPosition == 0) {\r\n // replace facet address with last facet address and delete last facet address\r\n uint256 lastFacetAddressPosition = ds.facetAddresses.length - 1;\r\n uint256 facetAddressPosition = ds\r\n .facetFunctionSelectors[_facetAddress]\r\n .facetAddressPosition;\r\n if (facetAddressPosition != lastFacetAddressPosition) {\r\n address lastFacetAddress = ds.facetAddresses[\r\n lastFacetAddressPosition\r\n ];\r\n ds.facetAddresses[facetAddressPosition] = lastFacetAddress;\r\n ds\r\n .facetFunctionSelectors[lastFacetAddress]\r\n .facetAddressPosition = facetAddressPosition;\r\n }\r\n ds.facetAddresses.pop();\r\n delete ds\r\n .facetFunctionSelectors[_facetAddress]\r\n .facetAddressPosition;\r\n }\r\n }\r\n\r\n function initializeDiamondCut(address _init, bytes memory _calldata)\r\n internal\r\n {\r\n if (_init == address(0)) {\r\n require(\r\n _calldata.length == 0,\r\n \"LibDiamondCut: _init is address(0) but_calldata is not empty\"\r\n );\r\n } else {\r\n require(\r\n _calldata.length > 0,\r\n \"LibDiamondCut: _calldata is empty but _init is not address(0)\"\r\n );\r\n if (_init != address(this)) {\r\n enforceHasContractCode(\r\n _init,\r\n \"LibDiamondCut: _init address has no code\"\r\n );\r\n }\r\n // solhint-disable-next-line avoid-low-level-calls\r\n (bool success, bytes memory error) = _init.delegatecall(_calldata);\r\n if (!success) {\r\n if (error.length > 0) {\r\n // bubble up the error\r\n revert(string(error));\r\n } else {\r\n revert(\"LibDiamondCut: _init function reverted\");\r\n }\r\n }\r\n }\r\n }\r\n\r\n function enforceHasContractCode(\r\n address _contract,\r\n string memory _errorMessage\r\n ) internal view {\r\n uint256 contractSize;\r\n // solhint-disable-next-line no-inline-assembly\r\n assembly {\r\n contractSize := extcodesize(_contract)\r\n }\r\n require(contractSize > 0, _errorMessage);\r\n }\r\n}\r\n"
},
"@openzeppelin/contracts/utils/Address.sol": {
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)\n\npragma solidity ^0.8.1;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n"
},
"src/bridges/interfaces/IDiamondCut.sol": {
"content": "// SPDX-License-Identifier: MIT\r\npragma solidity >=0.8.4 <0.9.0;\r\n\r\ninterface IDiamondCut {\r\n enum FacetCutAction {\r\n Add,\r\n Replace,\r\n Remove\r\n }\r\n // Add=0, Replace=1, Remove=2\r\n\r\n struct FacetCut {\r\n address facetAddress;\r\n FacetCutAction action;\r\n bytes4[] functionSelectors;\r\n }\r\n\r\n /// @notice Add/replace/remove any number of functions and optionally execute\r\n /// a function with delegatecall\r\n /// @param _diamondCut Contains the facet addresses and function selectors\r\n /// @param _init The address of the contract or facet to execute _calldata\r\n /// @param _calldata A function call, including function selector and arguments\r\n /// _calldata is executed with delegatecall on _init\r\n function diamondCut(\r\n FacetCut[] calldata _diamondCut,\r\n address _init,\r\n bytes calldata _calldata\r\n ) external;\r\n\r\n event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);\r\n}\r\n"
},
"src/bridges/libs/LibAsset.sol": {
"content": "// SPDX-License-Identifier: UNLICENSED\r\n// solhint-disable-next-line\r\npragma solidity 0.8.4;\r\nimport {NullAddrIsNotAnERC20Token, NullAddrIsNotAValidSpender, NoTransferToNullAddress, InvalidAmount, NativeValueWithERC, NativeAssetTransferFailed} from \"../errors/GenericErrors.sol\";\r\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\r\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\r\n\r\n/// @title LibAsset\r\n/// @author Connext <support@connext.network>\r\n/// @notice This library contains helpers for dealing with onchain transfers\r\n/// of assets, including accounting for the native asset `assetId`\r\n/// conventions and any noncompliant ERC20 transfers\r\nlibrary LibAsset {\r\n uint256 private constant MAX_INT = type(uint256).max;\r\n\r\n addre