Bridge.sol 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. // SPDX-License-Identifier: BUSL-1.1
  2. pragma solidity 0.7.6;
  3. pragma abicoder v2;
  4. // imports
  5. import "@openzeppelin/contracts/access/Ownable.sol";
  6. import "./Pool.sol";
  7. import "./Router.sol";
  8. // interfaces
  9. import "@layerzerolabs/contracts/contracts/interfaces/ILayerZeroReceiver.sol";
  10. import "@layerzerolabs/contracts/contracts/interfaces/ILayerZeroEndpoint.sol";
  11. import "@layerzerolabs/contracts/contracts/interfaces/ILayerZeroUserApplicationConfig.sol";
  12. // libraries
  13. import "@openzeppelin/contracts/math/SafeMath.sol";
  14. contract Bridge is Ownable, ILayerZeroReceiver, ILayerZeroUserApplicationConfig {
  15. using SafeMath for uint256;
  16. //---------------------------------------------------------------------------
  17. // CONSTANTS
  18. uint8 internal constant TYPE_SWAP_REMOTE = 1;
  19. uint8 internal constant TYPE_ADD_LIQUIDITY = 2;
  20. uint8 internal constant TYPE_REDEEM_LOCAL_CALL_BACK = 3;
  21. uint8 internal constant TYPE_WITHDRAW_REMOTE = 4;
  22. //---------------------------------------------------------------------------
  23. // VARIABLES
  24. ILayerZeroEndpoint public immutable layerZeroEndpoint;
  25. mapping(uint16 => bytes) public bridgeLookup;
  26. mapping(uint16 => mapping(uint8 => uint256)) public gasLookup;
  27. Router public immutable router;
  28. bool public useLayerZeroToken;
  29. //---------------------------------------------------------------------------
  30. // EVENTS
  31. event SendMsg(uint8 msgType, uint64 nonce);
  32. //---------------------------------------------------------------------------
  33. // MODIFIERS
  34. modifier onlyRouter() {
  35. require(msg.sender == address(router), "Stargate: caller must be Router.");
  36. _;
  37. }
  38. constructor(address _layerZeroEndpoint, address _router) {
  39. require(_layerZeroEndpoint != address(0x0), "Stargate: _layerZeroEndpoint cannot be 0x0");
  40. require(_router != address(0x0), "Stargate: _router cannot be 0x0");
  41. layerZeroEndpoint = ILayerZeroEndpoint(_layerZeroEndpoint);
  42. router = Router(_router);
  43. }
  44. //---------------------------------------------------------------------------
  45. // EXTERNAL functions
  46. function lzReceive(
  47. uint16 _srcChainId,
  48. bytes memory _srcAddress,
  49. uint64 _nonce,
  50. bytes memory _payload
  51. ) external override {
  52. require(msg.sender == address(layerZeroEndpoint), "Stargate: only LayerZero endpoint can call lzReceive");
  53. require(
  54. _srcAddress.length == bridgeLookup[_srcChainId].length && keccak256(_srcAddress) == keccak256(bridgeLookup[_srcChainId]),
  55. "Stargate: bridge does not match"
  56. );
  57. uint8 functionType;
  58. assembly {
  59. functionType := mload(add(_payload, 32))
  60. }
  61. if (functionType == TYPE_SWAP_REMOTE) {
  62. (
  63. ,
  64. uint256 srcPoolId,
  65. uint256 dstPoolId,
  66. uint256 dstGasForCall,
  67. Pool.CreditObj memory c,
  68. Pool.SwapObj memory s,
  69. bytes memory to,
  70. bytes memory payload
  71. ) = abi.decode(_payload, (uint8, uint256, uint256, uint256, Pool.CreditObj, Pool.SwapObj, bytes, bytes));
  72. address toAddress;
  73. assembly {
  74. toAddress := mload(add(to, 20))
  75. }
  76. router.creditChainPath(_srcChainId, srcPoolId, dstPoolId, c);
  77. router.swapRemote(_srcChainId, _srcAddress, _nonce, srcPoolId, dstPoolId, dstGasForCall, toAddress, s, payload);
  78. } else if (functionType == TYPE_ADD_LIQUIDITY) {
  79. (, uint256 srcPoolId, uint256 dstPoolId, Pool.CreditObj memory c) = abi.decode(_payload, (uint8, uint256, uint256, Pool.CreditObj));
  80. router.creditChainPath(_srcChainId, srcPoolId, dstPoolId, c);
  81. } else if (functionType == TYPE_REDEEM_LOCAL_CALL_BACK) {
  82. (, uint256 srcPoolId, uint256 dstPoolId, Pool.CreditObj memory c, uint256 amountSD, uint256 mintAmountSD, bytes memory to) = abi
  83. .decode(_payload, (uint8, uint256, uint256, Pool.CreditObj, uint256, uint256, bytes));
  84. address toAddress;
  85. assembly {
  86. toAddress := mload(add(to, 20))
  87. }
  88. router.creditChainPath(_srcChainId, srcPoolId, dstPoolId, c);
  89. router.redeemLocalCallback(_srcChainId, _srcAddress, _nonce, srcPoolId, dstPoolId, toAddress, amountSD, mintAmountSD);
  90. } else if (functionType == TYPE_WITHDRAW_REMOTE) {
  91. (, uint256 srcPoolId, uint256 dstPoolId, Pool.CreditObj memory c, uint256 amountSD, bytes memory to) = abi.decode(
  92. _payload,
  93. (uint8, uint256, uint256, Pool.CreditObj, uint256, bytes)
  94. );
  95. router.creditChainPath(_srcChainId, srcPoolId, dstPoolId, c);
  96. router.redeemLocalCheckOnRemote(_srcChainId, _srcAddress, _nonce, srcPoolId, dstPoolId, amountSD, to);
  97. }
  98. }
  99. //---------------------------------------------------------------------------
  100. // LOCAL CHAIN FUNCTIONS
  101. function swap(
  102. uint16 _chainId,
  103. uint256 _srcPoolId,
  104. uint256 _dstPoolId,
  105. address payable _refundAddress,
  106. Pool.CreditObj memory _c,
  107. Pool.SwapObj memory _s,
  108. IStargateRouter.lzTxObj memory _lzTxParams,
  109. bytes calldata _to,
  110. bytes calldata _payload
  111. ) external payable onlyRouter {
  112. bytes memory payload = abi.encode(TYPE_SWAP_REMOTE, _srcPoolId, _dstPoolId, _lzTxParams.dstGasForCall, _c, _s, _to, _payload);
  113. _call(_chainId, TYPE_SWAP_REMOTE, _refundAddress, _lzTxParams, payload);
  114. }
  115. function redeemLocalCallback(
  116. uint16 _chainId,
  117. address payable _refundAddress,
  118. Pool.CreditObj memory _c,
  119. IStargateRouter.lzTxObj memory _lzTxParams,
  120. bytes memory _payload
  121. ) external payable onlyRouter {
  122. bytes memory payload;
  123. {
  124. (, uint256 srcPoolId, uint256 dstPoolId, uint256 amountSD, uint256 mintAmountSD, bytes memory to) = abi.decode(
  125. _payload,
  126. (uint8, uint256, uint256, uint256, uint256, bytes)
  127. );
  128. // swap dst and src because we are headed back
  129. payload = abi.encode(TYPE_REDEEM_LOCAL_CALL_BACK, dstPoolId, srcPoolId, _c, amountSD, mintAmountSD, to);
  130. }
  131. _call(_chainId, TYPE_REDEEM_LOCAL_CALL_BACK, _refundAddress, _lzTxParams, payload);
  132. }
  133. function redeemLocal(
  134. uint16 _chainId,
  135. uint256 _srcPoolId,
  136. uint256 _dstPoolId,
  137. address payable _refundAddress,
  138. Pool.CreditObj memory _c,
  139. uint256 _amountSD,
  140. bytes calldata _to,
  141. IStargateRouter.lzTxObj memory _lzTxParams
  142. ) external payable onlyRouter {
  143. bytes memory payload = abi.encode(TYPE_WITHDRAW_REMOTE, _srcPoolId, _dstPoolId, _c, _amountSD, _to);
  144. _call(_chainId, TYPE_WITHDRAW_REMOTE, _refundAddress, _lzTxParams, payload);
  145. }
  146. function sendCredits(
  147. uint16 _chainId,
  148. uint256 _srcPoolId,
  149. uint256 _dstPoolId,
  150. address payable _refundAddress,
  151. Pool.CreditObj memory _c
  152. ) external payable onlyRouter {
  153. bytes memory payload = abi.encode(TYPE_ADD_LIQUIDITY, _srcPoolId, _dstPoolId, _c);
  154. IStargateRouter.lzTxObj memory lzTxObj = IStargateRouter.lzTxObj(0, 0, "0x");
  155. _call(_chainId, TYPE_ADD_LIQUIDITY, _refundAddress, lzTxObj, payload);
  156. }
  157. function quoteLayerZeroFee(
  158. uint16 _chainId,
  159. uint8 _functionType,
  160. bytes calldata _toAddress,
  161. bytes calldata _transferAndCallPayload,
  162. IStargateRouter.lzTxObj memory _lzTxParams
  163. ) external view returns (uint256, uint256) {
  164. bytes memory payload = "";
  165. Pool.CreditObj memory c = Pool.CreditObj(1, 1);
  166. if (_functionType == TYPE_SWAP_REMOTE) {
  167. Pool.SwapObj memory s = Pool.SwapObj(1, 1, 1, 1, 1, 1);
  168. payload = abi.encode(TYPE_SWAP_REMOTE, 0, 0, 0, c, s, _toAddress, _transferAndCallPayload);
  169. } else if (_functionType == TYPE_ADD_LIQUIDITY) {
  170. payload = abi.encode(TYPE_ADD_LIQUIDITY, 0, 0, c);
  171. } else if (_functionType == TYPE_REDEEM_LOCAL_CALL_BACK) {
  172. payload = abi.encode(TYPE_REDEEM_LOCAL_CALL_BACK, 0, 0, c, 0, 0, _toAddress);
  173. } else if (_functionType == TYPE_WITHDRAW_REMOTE) {
  174. payload = abi.encode(TYPE_WITHDRAW_REMOTE, 0, 0, c, 0, _toAddress);
  175. } else {
  176. revert("Stargate: unsupported function type");
  177. }
  178. bytes memory lzTxParamBuilt = _txParamBuilder(_chainId, _functionType, _lzTxParams);
  179. return layerZeroEndpoint.estimateFees(_chainId, address(this), payload, useLayerZeroToken, lzTxParamBuilt);
  180. }
  181. //---------------------------------------------------------------------------
  182. // dao functions
  183. function setBridge(uint16 _chainId, bytes calldata _bridgeAddress) external onlyOwner {
  184. require(bridgeLookup[_chainId].length == 0, "Stargate: Bridge already set!");
  185. bridgeLookup[_chainId] = _bridgeAddress;
  186. }
  187. function setGasAmount(
  188. uint16 _chainId,
  189. uint8 _functionType,
  190. uint256 _gasAmount
  191. ) external onlyOwner {
  192. require(_functionType >= 1 && _functionType <= 4, "Stargate: invalid _functionType");
  193. gasLookup[_chainId][_functionType] = _gasAmount;
  194. }
  195. function approveTokenSpender(
  196. address token,
  197. address spender,
  198. uint256 amount
  199. ) external onlyOwner {
  200. IERC20(token).approve(spender, amount);
  201. }
  202. function setUseLayerZeroToken(bool enable) external onlyOwner {
  203. useLayerZeroToken = enable;
  204. }
  205. function forceResumeReceive(uint16 _srcChainId, bytes calldata _srcAddress) external override onlyOwner {
  206. layerZeroEndpoint.forceResumeReceive(_srcChainId, _srcAddress);
  207. }
  208. //---------------------------------------------------------------------------
  209. // generic config for user Application
  210. function setConfig(
  211. uint16 _version,
  212. uint16 _chainId,
  213. uint256 _configType,
  214. bytes calldata _config
  215. ) external override onlyOwner {
  216. layerZeroEndpoint.setConfig(_version, _chainId, _configType, _config);
  217. }
  218. function setSendVersion(uint16 version) external override onlyOwner {
  219. layerZeroEndpoint.setSendVersion(version);
  220. }
  221. function setReceiveVersion(uint16 version) external override onlyOwner {
  222. layerZeroEndpoint.setReceiveVersion(version);
  223. }
  224. //---------------------------------------------------------------------------
  225. // INTERNAL functions
  226. function txParamBuilderType1(uint256 _gasAmount) internal pure returns (bytes memory) {
  227. uint16 txType = 1;
  228. return abi.encodePacked(txType, _gasAmount);
  229. }
  230. function txParamBuilderType2(
  231. uint256 _gasAmount,
  232. uint256 _dstNativeAmount,
  233. bytes memory _dstNativeAddr
  234. ) internal pure returns (bytes memory) {
  235. uint16 txType = 2;
  236. return abi.encodePacked(txType, _gasAmount, _dstNativeAmount, _dstNativeAddr);
  237. }
  238. function _txParamBuilder(
  239. uint16 _chainId,
  240. uint8 _type,
  241. IStargateRouter.lzTxObj memory _lzTxParams
  242. ) internal view returns (bytes memory) {
  243. bytes memory lzTxParam;
  244. address dstNativeAddr;
  245. {
  246. bytes memory dstNativeAddrBytes = _lzTxParams.dstNativeAddr;
  247. assembly {
  248. dstNativeAddr := mload(add(dstNativeAddrBytes, 20))
  249. }
  250. }
  251. uint256 totalGas = gasLookup[_chainId][_type].add(_lzTxParams.dstGasForCall);
  252. if (_lzTxParams.dstNativeAmount > 0 && dstNativeAddr != address(0x0)) {
  253. lzTxParam = txParamBuilderType2(totalGas, _lzTxParams.dstNativeAmount, _lzTxParams.dstNativeAddr);
  254. } else {
  255. lzTxParam = txParamBuilderType1(totalGas);
  256. }
  257. return lzTxParam;
  258. }
  259. function _call(
  260. uint16 _chainId,
  261. uint8 _type,
  262. address payable _refundAddress,
  263. IStargateRouter.lzTxObj memory _lzTxParams,
  264. bytes memory _payload
  265. ) internal {
  266. bytes memory lzTxParamBuilt = _txParamBuilder(_chainId, _type, _lzTxParams);
  267. uint64 nextNonce = layerZeroEndpoint.getOutboundNonce(_chainId, address(this)) + 1;
  268. layerZeroEndpoint.send{value: msg.value}(_chainId, bridgeLookup[_chainId], _payload, _refundAddress, address(this), lzTxParamBuilt);
  269. emit SendMsg(_type, nextNonce);
  270. }
  271. function renounceOwnership() public override onlyOwner {}
  272. }