Transaction Hash:
Block:
18232176 at Sep-28-2023 05:31:59 AM +UTC
Transaction Fee:
0.000168299905608 ETH
$0.42
Gas Used:
24,000 Gas / 7.012496067 Gwei
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x50981b37...A24B16925 |
0.083973126099181991 Eth
Nonce: 28
|
0.083804826193573991 Eth
Nonce: 29
| 0.000168299905608 | ||
0x95222290...5CC4BAfe5
Miner
| (beaverbuild) | 18.421991658178606459 Eth | 18.421998858178606459 Eth | 0.0000072 |
Execution Trace
ETH 0.070732926657822955
WooCrossChainRouterV2.crossSwap( refId=387591695879055483, to=0x50981b37d16f7090c0a968bD8AE9207A24B16925, srcInfos=[{name:fromToken, type:address, order:1, indexed:false, value:0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, valueString:0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE}, {name:bridgeToken, type:address, order:2, indexed:false, value:0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2, valueString:0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2}, {name:fromAmount, type:uint256, order:3, indexed:false, value:70000000000000007, valueString:70000000000000007}, {name:minBridgeAmount, type:uint256, order:4, indexed:false, value:70000000000000007, valueString:70000000000000007}], dstInfos=[{name:chainId, type:uint16, order:1, indexed:false, value:110, valueString:110}, {name:toToken, type:address, order:2, indexed:false, value:0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, valueString:0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE}, {name:bridgeToken, type:address, order:3, indexed:false, value:0x82aF49447D8a07e3bd95BD0d56f35241523fBab1, valueString:0x82aF49447D8a07e3bd95BD0d56f35241523fBab1}, {name:minToAmount, type:uint256, order:4, indexed:false, value:69297029999999996, valueString:69297029999999996}, {name:airdropNativeAmount, type:uint256, order:5, indexed:false, value:0, valueString:0}] )
crossSwap[WooCrossChainRouterV2 (ln:56)]
deposit[WooCrossChainRouterV2 (ln:75)]
safeTransferFrom[WooCrossChainRouterV2 (ln:78)]
call[TransferHelper (ln:811)]
encodeWithSelector[TransferHelper (ln:811)]
decode[TransferHelper (ln:813)]
contains[WooCrossChainRouterV2 (ln:84)]
safeApprove[WooCrossChainRouterV2 (ln:85)]
call[TransferHelper (ln:786)]
encodeWithSelector[TransferHelper (ln:786)]
decode[TransferHelper (ln:788)]
swap[WooCrossChainRouterV2 (ln:86)]
payable[WooCrossChainRouterV2 (ln:91)]
balanceOf[WooCrossChainRouterV2 (ln:102)]
_bridgeByOFT[WooCrossChainRouterV2 (ln:109)]
token[WooCrossChainRouterV2 (ln:304)]
safeApprove[WooCrossChainRouterV2 (ln:309)]
call[TransferHelper (ln:786)]
encodeWithSelector[TransferHelper (ln:786)]
decode[TransferHelper (ln:788)]
encode[WooCrossChainRouterV2 (ln:314)]
_getDstGasForCall[WooCrossChainRouterV2 (ln:315)]
_getAdapterParams[WooCrossChainRouterV2 (ln:318)]
minDstGasLookup[WooCrossChainRouterV2 (ln:277)]
LzCallParams[WooCrossChainRouterV2 (ln:319)]
payable[WooCrossChainRouterV2 (ln:320)]
sendAndCall[WooCrossChainRouterV2 (ln:326)]
_bridgeByStargate[WooCrossChainRouterV2 (ln:111)]
encode[WooCrossChainRouterV2 (ln:349)]
_getLzTxObj[WooCrossChainRouterV2 (ln:352)]
_getDstGasForCall[WooCrossChainRouterV2 (ln:288)]
lzTxObj[WooCrossChainRouterV2 (ln:289)]
withdraw[WooCrossChainRouterV2 (ln:354)]
deposit[WooCrossChainRouterV2 (ln:356)]
safeApprove[WooCrossChainRouterV2 (ln:357)]
call[TransferHelper (ln:786)]
encodeWithSelector[TransferHelper (ln:786)]
decode[TransferHelper (ln:788)]
safeApprove[WooCrossChainRouterV2 (ln:359)]
call[TransferHelper (ln:786)]
encodeWithSelector[TransferHelper (ln:786)]
decode[TransferHelper (ln:788)]
swap[WooCrossChainRouterV2 (ln:361)]
payable[WooCrossChainRouterV2 (ln:365)]
_msgSender[WooCrossChainRouterV2 (ln:365)]
WooCrossSwapOnSrcChain[WooCrossChainRouterV2 (ln:113)]
_msgSender[WooCrossChainRouterV2 (ln:115)]
// SPDX-License-Identifier: MIT pragma solidity =0.8.14; // OpenZeppelin Contracts import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import {ICommonOFT, IOFTWithFee} from "@layerzerolabs/solidity-examples/contracts/token/oft/v2/fee/IOFTWithFee.sol"; // Local Contracts import {IWETH} from "./interfaces/IWETH.sol"; import {IWooCrossChainRouterV2} from "./interfaces/IWooCrossChainRouterV2.sol"; import {IWooRouterV2} from "./interfaces/IWooRouterV2.sol"; import {IStargateEthVault} from "./interfaces/Stargate/IStargateEthVault.sol"; import {IStargateRouter} from "./interfaces/Stargate/IStargateRouter.sol"; import {ILzApp} from "./interfaces/LayerZero/ILzApp.sol"; import {TransferHelper} from "./libraries/TransferHelper.sol"; /// @title WOOFi cross chain router implementation. /// @notice Router for stateless execution of cross chain swap against WOOFi private pool. /// @custom:stargate-contracts https://stargateprotocol.gitbook.io/stargate/developers/contract-addresses/mainnet contract WooCrossChainRouterV2 is IWooCrossChainRouterV2, Ownable, ReentrancyGuard { using EnumerableSet for EnumerableSet.AddressSet; /* ----- Constants ----- */ address public constant ETH_PLACEHOLDER_ADDR = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; /* ----- Variables ----- */ IWooRouterV2 public wooRouter; IStargateRouter public stargateRouter; address public immutable weth; uint256 public bridgeSlippage; // 1 in 10000th: default 1% uint256 public dstGasForSwapCall; uint256 public dstGasForNoSwapCall; uint16 public sgChainIdLocal; // Stargate chainId on local chain mapping(uint16 => address) public wooCrossChainRouters; // chainId => WooCrossChainRouter address mapping(uint16 => address) public sgETHs; // chainId => SGETH token address mapping(uint16 => mapping(address => uint256)) public sgPoolIds; // chainId => token address => Stargate poolId mapping(address => address) public tokenToOFTs; // token address(sgChainIdLocal) => OFT address EnumerableSet.AddressSet private directBridgeTokens; receive() external payable {} constructor( address _weth, address _wooRouter, address _stargateRouter, uint16 _sgChainIdLocal ) { wooRouter = IWooRouterV2(_wooRouter); stargateRouter = IStargateRouter(_stargateRouter); weth = _weth; bridgeSlippage = 100; dstGasForSwapCall = 360000; dstGasForNoSwapCall = 80000; sgChainIdLocal = _sgChainIdLocal; _initSgETHs(); _initSgPoolIds(); _initTokenToOFTs(_sgChainIdLocal); } /* ----- Functions ----- */ function crossSwap( uint256 refId, address payable to, SrcInfos memory srcInfos, DstInfos memory dstInfos ) external payable nonReentrant { require(srcInfos.fromToken != address(0), "WooCrossChainRouterV2: !srcInfos.fromToken"); require( dstInfos.toToken != address(0) && dstInfos.toToken != sgETHs[dstInfos.chainId], "WooCrossChainRouterV2: !dstInfos.toToken" ); require(to != address(0), "WooCrossChainRouterV2: !to"); uint256 msgValue = msg.value; uint256 bridgeAmount; { // Step 1: transfer if (srcInfos.fromToken == ETH_PLACEHOLDER_ADDR) { require(srcInfos.fromAmount <= msgValue, "WooCrossChainRouterV2: !srcInfos.fromAmount"); srcInfos.fromToken = weth; IWETH(weth).deposit{value: srcInfos.fromAmount}(); msgValue -= srcInfos.fromAmount; } else { TransferHelper.safeTransferFrom(srcInfos.fromToken, msg.sender, address(this), srcInfos.fromAmount); } // Step 2: local swap by WooRouter or not // 1.WOO is directBridgeToken, path(always) WOO(Arbitrum) => WOO(BSC) // 2.WOO not the directBridgeToken, path(maybe): WOO(Arbitrum) -> USDC(Arbitrum) => BUSD(BSC) -> WOO(BSC) // 3.Ethereum no WOOFi liquidity, tokens(WOO, ETH, USDC) always will be bridged directly without swap if (!directBridgeTokens.contains(srcInfos.fromToken) && srcInfos.fromToken != srcInfos.bridgeToken) { TransferHelper.safeApprove(srcInfos.fromToken, address(wooRouter), srcInfos.fromAmount); bridgeAmount = wooRouter.swap( srcInfos.fromToken, srcInfos.bridgeToken, srcInfos.fromAmount, srcInfos.minBridgeAmount, payable(address(this)), to ); } else { require( srcInfos.fromAmount == srcInfos.minBridgeAmount, "WooCrossChainRouterV2: !srcInfos.minBridgeAmount" ); bridgeAmount = srcInfos.fromAmount; } require( bridgeAmount <= IERC20(srcInfos.bridgeToken).balanceOf(address(this)), "WooCrossChainRouterV2: !bridgeAmount" ); } // Step 3: cross chain swap by [OFT / StargateRouter] address oft = tokenToOFTs[srcInfos.bridgeToken]; if (oft != address(0)) { _bridgeByOFT(refId, to, msgValue, bridgeAmount, IOFTWithFee(oft), srcInfos, dstInfos); } else { _bridgeByStargate(refId, to, msgValue, bridgeAmount, srcInfos, dstInfos); } emit WooCrossSwapOnSrcChain( refId, _msgSender(), to, srcInfos.fromToken, srcInfos.fromAmount, srcInfos.minBridgeAmount, bridgeAmount ); } function onOFTReceived( uint16 srcChainId, bytes memory, // srcAddress uint64, // nonce bytes32 from, uint256 amountLD, bytes memory payload ) external { require(_isLegitOFT(_msgSender()), "WooCrossChainRouterV2: INVALID_CALLER"); require( wooCrossChainRouters[srcChainId] == address(uint160(uint256(from))), "WooCrossChainRouterV2: INVALID_FROM" ); // _msgSender() should be OFT address if requires above are passed address bridgedToken = IOFTWithFee(_msgSender()).token(); // make sure the same order to abi.encode when decode payload (uint256 refId, address to, address toToken, uint256 minToAmount) = abi.decode( payload, (uint256, address, address, uint256) ); _handleERC20Received(refId, to, toToken, bridgedToken, amountLD, minToAmount); } function sgReceive( uint16, // srcChainId bytes memory, // srcAddress uint256, // nonce address bridgedToken, uint256 amountLD, bytes memory payload ) external { require(msg.sender == address(stargateRouter), "WooCrossChainRouterV2: INVALID_CALLER"); // make sure the same order to abi.encode when decode payload (uint256 refId, address to, address toToken, uint256 minToAmount) = abi.decode( payload, (uint256, address, address, uint256) ); // toToken won't be SGETH, and bridgedToken won't be ETH_PLACEHOLDER_ADDR if (bridgedToken == sgETHs[sgChainIdLocal]) { // bridgedToken is SGETH, received native token _handleNativeReceived(refId, to, toToken, amountLD, minToAmount); } else { // bridgedToken is not SGETH, received ERC20 token _handleERC20Received(refId, to, toToken, bridgedToken, amountLD, minToAmount); } } function quoteLayerZeroFee( uint256 refId, address to, SrcInfos memory srcInfos, DstInfos memory dstInfos ) external view returns (uint256, uint256) { bytes memory payload = abi.encode(refId, to, dstInfos.toToken, dstInfos.minToAmount); address oft = tokenToOFTs[srcInfos.bridgeToken]; if (oft != address(0)) { // bridge via OFT if it's OFT uint256 dstGasForCall = _getDstGasForCall(dstInfos); bytes memory adapterParams = _getAdapterParams(to, oft, dstGasForCall, dstInfos); bool useZro = false; bytes32 dstWooCrossChainRouter = bytes32(uint256(uint160(wooCrossChainRouters[dstInfos.chainId]))); return IOFTWithFee(oft).estimateSendAndCallFee( dstInfos.chainId, dstWooCrossChainRouter, srcInfos.minBridgeAmount, payload, uint64(dstGasForCall), useZro, adapterParams ); } else { // otherwise bridge via Stargate IStargateRouter.lzTxObj memory obj = _getLzTxObj(to, dstInfos); return stargateRouter.quoteLayerZeroFee( dstInfos.chainId, 1, // https://stargateprotocol.gitbook.io/stargate/developers/function-types obj.dstNativeAddr, payload, obj ); } } function allDirectBridgeTokens() external view returns (address[] memory) { uint256 length = directBridgeTokens.length(); address[] memory tokens = new address[](length); unchecked { for (uint256 i = 0; i < length; ++i) { tokens[i] = directBridgeTokens.at(i); } } return tokens; } function allDirectBridgeTokensLength() external view returns (uint256) { return directBridgeTokens.length(); } function _initSgETHs() internal { // Ethereum sgETHs[101] = 0x72E2F4830b9E45d52F80aC08CB2bEC0FeF72eD9c; // Arbitrum sgETHs[110] = 0x82CbeCF39bEe528B5476FE6d1550af59a9dB6Fc0; // Optimism sgETHs[111] = 0xb69c8CBCD90A39D8D3d3ccf0a3E968511C3856A0; } function _initSgPoolIds() internal { // poolId > 0 means able to be bridge token // Ethereum sgPoolIds[101][0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48] = 1; // USDC sgPoolIds[101][0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2] = 13; // WETH sgPoolIds[101][0x4691937a7508860F876c9c0a2a617E7d9E945D4B] = 20; // WOO // BNB Chain sgPoolIds[102][0x55d398326f99059fF775485246999027B3197955] = 2; // USDT sgPoolIds[102][0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56] = 5; // BUSD sgPoolIds[102][0x4691937a7508860F876c9c0a2a617E7d9E945D4B] = 20; // WOO // Avalanche sgPoolIds[106][0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E] = 1; // USDC sgPoolIds[106][0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7] = 2; // USDT sgPoolIds[106][0xaBC9547B534519fF73921b1FBA6E672b5f58D083] = 20; // WOO // Polygon sgPoolIds[109][0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174] = 1; // USDC sgPoolIds[109][0xc2132D05D31c914a87C6611C10748AEb04B58e8F] = 2; // USDT sgPoolIds[109][0x1B815d120B3eF02039Ee11dC2d33DE7aA4a8C603] = 20; // WOO // Arbitrum sgPoolIds[110][0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8] = 1; // USDC sgPoolIds[110][0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9] = 2; // USDT sgPoolIds[110][0x82aF49447D8a07e3bd95BD0d56f35241523fBab1] = 13; // WETH sgPoolIds[110][0xcAFcD85D8ca7Ad1e1C6F82F651fA15E33AEfD07b] = 20; // WOO // Optimism sgPoolIds[111][0x7F5c764cBc14f9669B88837ca1490cCa17c31607] = 1; // USDC sgPoolIds[111][0x4200000000000000000000000000000000000006] = 13; // WETH sgPoolIds[111][0x871f2F2ff935FD1eD867842FF2a7bfD051A5E527] = 20; // WOO // Fantom sgPoolIds[112][0x04068DA6C83AFCFA0e13ba15A6696662335D5B75] = 1; // USDC sgPoolIds[112][0x6626c47c00F1D87902fc13EECfaC3ed06D5E8D8a] = 20; // WOO } function _initTokenToOFTs(uint16 _sgChainIdLocal) internal { address btcbOFT = 0x2297aEbD383787A160DD0d9F71508148769342E3; // BTCbOFT && BTCbProxyOFT if (_sgChainIdLocal == 106) { // BTC.b(ERC20) on Avalanche address tokenToOFTs[0x152b9d0FdC40C096757F570A51E494bd4b943E50] = btcbOFT; } tokenToOFTs[btcbOFT] = btcbOFT; } function _getDstGasForCall(DstInfos memory dstInfos) internal view returns (uint256) { return (dstInfos.toToken == dstInfos.bridgeToken) ? dstGasForNoSwapCall : dstGasForSwapCall; } function _getAdapterParams( address to, address oft, uint256 dstGasForCall, DstInfos memory dstInfos ) internal view returns (bytes memory) { // OFT src logic: require(providedGasLimit >= minGasLimit) // uint256 minGasLimit = minDstGasLookup[_dstChainId][_type] + dstGasForCall; // _type: 0(send), 1(send_and_call) uint256 providedGasLimit = ILzApp(oft).minDstGasLookup(dstInfos.chainId, 1) + dstGasForCall; // https://layerzero.gitbook.io/docs/evm-guides/advanced/relayer-adapter-parameters#airdrop return abi.encodePacked( uint16(2), // version: 2 is able to airdrop native token on destination but 1 is not providedGasLimit, // gasAmount: destination transaction gas for LayerZero to delivers dstInfos.airdropNativeAmount, // nativeForDst: airdrop native token amount to // addressOnDst: address to receive airdrop native token on destination ); } function _getLzTxObj(address to, DstInfos memory dstInfos) internal view returns (IStargateRouter.lzTxObj memory) { uint256 dstGasForCall = _getDstGasForCall(dstInfos); return IStargateRouter.lzTxObj(dstGasForCall, dstInfos.airdropNativeAmount, abi.encodePacked(to)); } function _isLegitOFT(address caller) internal view returns (bool) { return tokenToOFTs[caller] != address(0); } function _bridgeByOFT( uint256 refId, address payable to, uint256 msgValue, uint256 bridgeAmount, IOFTWithFee oft, SrcInfos memory srcInfos, DstInfos memory dstInfos ) internal { { address token = oft.token(); require(token == srcInfos.bridgeToken, "WooCrossChainRouterV2: !token"); if (token != address(oft)) { // oft.token() != address(oft) means is a ProxyOFT // for example: BTC.b on Avalanche is ERC20, need BTCbProxyOFT to lock up BTC.b TransferHelper.safeApprove(srcInfos.bridgeToken, address(oft), bridgeAmount); } } // OFT src logic: require(_removeDust(bridgeAmount) >= minAmount) uint256 minAmount = (bridgeAmount * (10000 - bridgeSlippage)) / 10000; bytes memory payload = abi.encode(refId, to, dstInfos.toToken, dstInfos.minToAmount); uint256 dstGasForCall = _getDstGasForCall(dstInfos); ICommonOFT.LzCallParams memory callParams; { bytes memory adapterParams = _getAdapterParams(to, address(oft), dstGasForCall, dstInfos); callParams = ICommonOFT.LzCallParams( payable(msg.sender), // refundAddress address(0), // zroPaymentAddress adapterParams //adapterParams ); } bytes32 dstWooCrossChainRouter = bytes32(uint256(uint160(wooCrossChainRouters[dstInfos.chainId]))); oft.sendAndCall{value: msgValue}( address(this), dstInfos.chainId, dstWooCrossChainRouter, bridgeAmount, minAmount, payload, uint64(dstGasForCall), callParams ); } function _bridgeByStargate( uint256 refId, address payable to, uint256 msgValue, uint256 bridgeAmount, SrcInfos memory srcInfos, DstInfos memory dstInfos ) internal { uint256 srcPoolId = sgPoolIds[sgChainIdLocal][srcInfos.bridgeToken]; require(srcPoolId > 0, "WooCrossChainRouterV2: !srcInfos.bridgeToken"); uint256 dstPoolId = sgPoolIds[dstInfos.chainId][dstInfos.bridgeToken]; require(dstPoolId > 0, "WooCrossChainRouterV2: !dstInfos.bridgeToken"); bytes memory payload = abi.encode(refId, to, dstInfos.toToken, dstInfos.minToAmount); uint256 dstMinBridgeAmount = (bridgeAmount * (10000 - bridgeSlippage)) / 10000; bytes memory dstWooCrossChainRouter = abi.encodePacked(wooCrossChainRouters[dstInfos.chainId]); IStargateRouter.lzTxObj memory obj = _getLzTxObj(to, dstInfos); if (srcInfos.bridgeToken == weth) { IWETH(weth).withdraw(bridgeAmount); address sgETH = sgETHs[sgChainIdLocal]; IStargateEthVault(sgETH).deposit{value: bridgeAmount}(); // logic from Stargate RouterETH.sol TransferHelper.safeApprove(sgETH, address(stargateRouter), bridgeAmount); } else { TransferHelper.safeApprove(srcInfos.bridgeToken, address(stargateRouter), bridgeAmount); } stargateRouter.swap{value: msgValue}( dstInfos.chainId, // dst chain id srcPoolId, // bridge token's pool id on src chain dstPoolId, // bridge token's pool id on dst chain payable(_msgSender()), // rebate address bridgeAmount, // swap amount on src chain dstMinBridgeAmount, // min received amount on dst chain obj, // config: dstGasForCall, dstAirdropNativeAmount, dstReceiveAirdropNativeTokenAddr dstWooCrossChainRouter, // smart contract to call on dst chain payload // payload to piggyback ); } function _handleNativeReceived( uint256 refId, address to, address toToken, uint256 bridgedAmount, uint256 minToAmount ) internal { address msgSender = _msgSender(); if (toToken == ETH_PLACEHOLDER_ADDR) { TransferHelper.safeTransferETH(to, bridgedAmount); emit WooCrossSwapOnDstChain( refId, msgSender, to, weth, bridgedAmount, toToken, ETH_PLACEHOLDER_ADDR, minToAmount, bridgedAmount ); } else { try wooRouter.swap{value: bridgedAmount}( ETH_PLACEHOLDER_ADDR, toToken, bridgedAmount, minToAmount, payable(to), to ) returns (uint256 realToAmount) { emit WooCrossSwapOnDstChain( refId, msgSender, to, weth, bridgedAmount, toToken, toToken, minToAmount, realToAmount ); } catch { TransferHelper.safeTransferETH(to, bridgedAmount); emit WooCrossSwapOnDstChain( refId, msgSender, to, weth, bridgedAmount, toToken, ETH_PLACEHOLDER_ADDR, minToAmount, bridgedAmount ); } } } function _handleERC20Received( uint256 refId, address to, address toToken, address bridgedToken, uint256 bridgedAmount, uint256 minToAmount ) internal { address msgSender = _msgSender(); if (toToken == bridgedToken) { TransferHelper.safeTransfer(bridgedToken, to, bridgedAmount); emit WooCrossSwapOnDstChain( refId, msgSender, to, bridgedToken, bridgedAmount, toToken, toToken, minToAmount, bridgedAmount ); } else { TransferHelper.safeApprove(bridgedToken, address(wooRouter), bridgedAmount); try wooRouter.swap(bridgedToken, toToken, bridgedAmount, minToAmount, payable(to), to) returns ( uint256 realToAmount ) { emit WooCrossSwapOnDstChain( refId, msgSender, to, bridgedToken, bridgedAmount, toToken, toToken, minToAmount, realToAmount ); } catch { TransferHelper.safeTransfer(bridgedToken, to, bridgedAmount); emit WooCrossSwapOnDstChain( refId, msgSender, to, bridgedToken, bridgedAmount, toToken, bridgedToken, minToAmount, bridgedAmount ); } } } /* ----- Owner & Admin Functions ----- */ function setWooRouter(address _wooRouter) external onlyOwner { require(_wooRouter != address(0), "WooCrossChainRouterV2: !_wooRouter"); wooRouter = IWooRouterV2(_wooRouter); } function setStargateRouter(address _stargateRouter) external onlyOwner { require(_stargateRouter != address(0), "WooCrossChainRouterV2: !_stargateRouter"); stargateRouter = IStargateRouter(_stargateRouter); } function setBridgeSlippage(uint256 _bridgeSlippage) external onlyOwner { require(_bridgeSlippage <= 10000, "WooCrossChainRouterV2: !_bridgeSlippage"); bridgeSlippage = _bridgeSlippage; } function setDstGasForSwapCall(uint256 _dstGasForSwapCall) external onlyOwner { dstGasForSwapCall = _dstGasForSwapCall; } function setDstGasForNoSwapCall(uint256 _dstGasForNoSwapCall) external onlyOwner { dstGasForNoSwapCall = _dstGasForNoSwapCall; } function setSgChainIdLocal(uint16 _sgChainIdLocal) external onlyOwner { sgChainIdLocal = _sgChainIdLocal; } function setWooCrossChainRouter(uint16 chainId, address wooCrossChainRouter) external onlyOwner { require(wooCrossChainRouter != address(0), "WooCrossChainRouterV2: !wooCrossChainRouter"); wooCrossChainRouters[chainId] = wooCrossChainRouter; } function setSgETH(uint16 chainId, address token) external onlyOwner { require(token != address(0), "WooCrossChainRouterV2: !token"); sgETHs[chainId] = token; } function setSgPoolId( uint16 chainId, address token, uint256 poolId ) external onlyOwner { sgPoolIds[chainId][token] = poolId; } function setTokenToOFT(address token, address oft) external onlyOwner { tokenToOFTs[token] = oft; } function addDirectBridgeToken(address token) external onlyOwner { bool success = directBridgeTokens.add(token); require(success, "WooCrossChainRouterV2: token exist"); } function removeDirectBridgeToken(address token) external onlyOwner { bool success = directBridgeTokens.remove(token); require(success, "WooCrossChainRouterV2: token not exist"); } function inCaseTokenGotStuck(address stuckToken) external onlyOwner { if (stuckToken == ETH_PLACEHOLDER_ADDR) { TransferHelper.safeTransferETH(msg.sender, address(this).balance); } else { uint256 amount = IERC20(stuckToken).balanceOf(address(this)); TransferHelper.safeTransfer(stuckToken, msg.sender, amount); } } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /// @title Wrapped ETH. interface IWETH { /// @dev Deposit ETH into WETH function deposit() external payable; /// @dev Transfer WETH to receiver /// @param to address of WETH receiver /// @param value amount of WETH to transfer /// @return get true when succeed, else false function transfer(address to, uint256 value) external returns (bool); /// @dev Withdraw WETH to ETH function withdraw(uint256) external; } // SPDX-License-Identifier: MIT pragma solidity =0.8.14; /* ░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗ ░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║ ░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║ ░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║ ░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║ ░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝ * * MIT License * =========== * * Copyright (c) 2020 WooTrade * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /// @title WOOFi cross chain router interface (version 2). /// @notice functions to interface with WOOFi cross chain swap. interface IWooCrossChainRouterV2 { /* ----- Structs ----- */ struct SrcInfos { address fromToken; address bridgeToken; uint256 fromAmount; uint256 minBridgeAmount; } struct DstInfos { uint16 chainId; address toToken; address bridgeToken; uint256 minToAmount; uint256 airdropNativeAmount; } /* ----- Events ----- */ event WooCrossSwapOnSrcChain( uint256 indexed refId, address indexed sender, address indexed to, address fromToken, uint256 fromAmount, uint256 minBridgeAmount, uint256 realBridgeAmount ); event WooCrossSwapOnDstChain( uint256 indexed refId, address indexed sender, address indexed to, address bridgedToken, uint256 bridgedAmount, address toToken, address realToToken, uint256 minToAmount, uint256 realToAmount ); /* ----- State Variables ----- */ function weth() external view returns (address); function bridgeSlippage() external view returns (uint256); function dstGasForSwapCall() external view returns (uint256); function dstGasForNoSwapCall() external view returns (uint256); function sgChainIdLocal() external view returns (uint16); function wooCrossChainRouters(uint16 chainId) external view returns (address wooCrossChainRouter); function sgETHs(uint16 chainId) external view returns (address sgETH); function sgPoolIds(uint16 chainId, address token) external view returns (uint256 poolId); /* ----- Functions ----- */ function crossSwap( uint256 refId, address payable to, SrcInfos memory srcInfos, DstInfos memory dstInfos ) external payable; function sgReceive( uint16 srcChainId, bytes memory srcAddress, uint256 nonce, address bridgedToken, uint256 amountLD, bytes memory payload ) external; function quoteLayerZeroFee( uint256 refId, address to, SrcInfos memory srcInfos, DstInfos memory dstInfos ) external view returns (uint256 nativeAmount, uint256 zroAmount); function allDirectBridgeTokens() external view returns (address[] memory tokens); function allDirectBridgeTokensLength() external view returns (uint256 length); } // SPDX-License-Identifier: MIT pragma solidity =0.8.14; /* ░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗ ░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║ ░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║ ░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║ ░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║ ░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝ * * MIT License * =========== * * Copyright (c) 2020 WooTrade * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ import "../interfaces/IWooPPV2.sol"; /// @title Woo router interface (version 2) /// @notice functions to interface with WooFi swap interface IWooRouterV2 { /* ----- Type declarations ----- */ enum SwapType { WooSwap, DodoSwap } /* ----- Events ----- */ event WooRouterSwap( SwapType swapType, address indexed fromToken, address indexed toToken, uint256 fromAmount, uint256 toAmount, address from, address indexed to, address rebateTo ); event WooPoolChanged(address newPool); /* ----- Router properties ----- */ function WETH() external view returns (address); function wooPool() external view returns (IWooPPV2); /* ----- Main query & swap APIs ----- */ /// @notice query the amount to swap fromToken -> toToken /// @param fromToken the from token /// @param toToken the to token /// @param fromAmount the amount of fromToken to swap /// @return toAmount the predicted amount to receive function querySwap( address fromToken, address toToken, uint256 fromAmount ) external view returns (uint256 toAmount); /// @notice query the amount to swap fromToken -> toToken, /// WITHOUT checking the reserve balance; so it /// always returns the quoted amount (for reference). /// @param fromToken the from token /// @param toToken the to token /// @param fromAmount the amount of fromToken to swap /// @return toAmount the predicted amount to receive function tryQuerySwap( address fromToken, address toToken, uint256 fromAmount ) external view returns (uint256 toAmount); /// @notice Swap `fromToken` to `toToken`. /// @param fromToken the from token /// @param toToken the to token /// @param fromAmount the amount of `fromToken` to swap /// @param minToAmount the minimum amount of `toToken` to receive /// @param to the destination address /// @param rebateTo the rebate address (optional, can be 0) /// @return realToAmount the amount of toToken to receive function swap( address fromToken, address toToken, uint256 fromAmount, uint256 minToAmount, address payable to, address rebateTo ) external payable returns (uint256 realToAmount); /* ----- 3rd party DEX swap ----- */ /// @notice swap fromToken -> toToken via an external 3rd swap /// @param approveTarget the contract address for token transfer approval /// @param swapTarget the contract address for swap /// @param fromToken the from token /// @param toToken the to token /// @param fromAmount the amount of fromToken to swap /// @param minToAmount the min amount of swapped toToken /// @param to the destination address /// @param data call data for external call function externalSwap( address approveTarget, address swapTarget, address fromToken, address toToken, uint256 fromAmount, uint256 minToAmount, address payable to, bytes calldata data ) external payable returns (uint256 realToAmount); } // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.0; // helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false library TransferHelper { function safeApprove( address token, address to, uint256 value ) internal { // bytes4(keccak256(bytes('approve(address,uint256)'))); (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value)); require( success && (data.length == 0 || abi.decode(data, (bool))), "TransferHelper::safeApprove: approve failed" ); } function safeTransfer( address token, address to, uint256 value ) internal { // bytes4(keccak256(bytes('transfer(address,uint256)'))); (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value)); require( success && (data.length == 0 || abi.decode(data, (bool))), "TransferHelper::safeTransfer: transfer failed" ); } function safeTransferFrom( address token, address from, address to, uint256 value ) internal { // bytes4(keccak256(bytes('transferFrom(address,address,uint256)'))); (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value)); require( success && (data.length == 0 || abi.decode(data, (bool))), "TransferHelper::transferFrom: transferFrom failed" ); } function safeTransferETH(address to, uint256 value) internal { (bool success, ) = to.call{value: value}(new bytes(0)); require(success, "TransferHelper::safeTransferETH: ETH transfer failed"); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IStargateEthVault { function deposit() external payable; function transfer(address to, uint256 value) external returns (bool); function withdraw(uint256) external; function approve(address guy, uint256 wad) external returns (bool); function transferFrom( address src, address dst, uint256 wad ) external returns (bool); } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IStargateRouter { struct lzTxObj { uint256 dstGasForCall; uint256 dstNativeAmount; bytes dstNativeAddr; } function addLiquidity( uint256 _poolId, uint256 _amountLD, address _to ) external; function swap( uint16 _dstChainId, uint256 _srcPoolId, uint256 _dstPoolId, address payable _refundAddress, uint256 _amountLD, uint256 _minAmountLD, lzTxObj memory _lzTxParams, bytes calldata _to, bytes calldata _payload ) external payable; function redeemRemote( uint16 _dstChainId, uint256 _srcPoolId, uint256 _dstPoolId, address payable _refundAddress, uint256 _amountLP, uint256 _minAmountLD, bytes calldata _to, lzTxObj memory _lzTxParams ) external payable; function instantRedeemLocal( uint16 _srcPoolId, uint256 _amountLP, address _to ) external returns (uint256); function redeemLocal( uint16 _dstChainId, uint256 _srcPoolId, uint256 _dstPoolId, address payable _refundAddress, uint256 _amountLP, bytes calldata _to, lzTxObj memory _lzTxParams ) external payable; function sendCredits( uint16 _dstChainId, uint256 _srcPoolId, uint256 _dstPoolId, address payable _refundAddress ) external payable; function quoteLayerZeroFee( uint16 _dstChainId, uint8 _functionType, bytes calldata _toAddress, bytes calldata _transferAndCallPayload, lzTxObj memory _lzTxParams ) external view returns (uint256, uint256); } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @dev Interface of the LzApp that functions not exist in the @layerzerolabs package */ interface ILzApp { function minDstGasLookup(uint16 _dstChainId, uint16 _type) external view returns (uint256 _minGasLimit); } // SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol) pragma solidity ^0.8.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. */ abstract contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor() { _transferOwnership(_msgSender()); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { 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. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } } // SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol) pragma solidity ^0.8.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]. */ abstract contract ReentrancyGuard { // 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. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _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 making it call a * `private` function that does the actual work. */ modifier nonReentrant() { // On the first call to nonReentrant, _notEntered will be true require(_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; } } // SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed 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. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (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. */ function allowance(address owner, address spender) external view returns (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. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` 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. */ function transferFrom( address from, address to, uint256 amount ) external returns (bool); } // SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol) pragma solidity ^0.8.0; /** * @dev Library for managing * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive * types. * * Sets have the following properties: * * - Elements are added, removed, and checked for existence in constant time * (O(1)). * - Elements are enumerated in O(n). No guarantees are made on the ordering. * * ``` * contract Example { * // Add the library methods * using EnumerableSet for EnumerableSet.AddressSet; * * // Declare a set state variable * EnumerableSet.AddressSet private mySet; * } * ``` * * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) * and `uint256` (`UintSet`) are supported. * * [WARNING] * ==== * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure unusable. * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. * * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an array of EnumerableSet. * ==== */ library EnumerableSet { // To implement this library for multiple types with as little code // repetition as possible, we write it in terms of a generic Set type with // bytes32 values. // The Set implementation uses private functions, and user-facing // implementations (such as AddressSet) are just wrappers around the // underlying Set. // This means that we can only create new EnumerableSets for types that fit // in bytes32. struct Set { // Storage of set values bytes32[] _values; // Position of the value in the `values` array, plus 1 because index 0 // means a value is not in the set. mapping(bytes32 => uint256) _indexes; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function _add(Set storage set, bytes32 value) private returns (bool) { if (!_contains(set, value)) { set._values.push(value); // The value is stored at length-1, but we add 1 to all indexes // and use 0 as a sentinel value set._indexes[value] = set._values.length; return true; } else { return false; } } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function _remove(Set storage set, bytes32 value) private returns (bool) { // We read and store the value's index to prevent multiple reads from the same storage slot uint256 valueIndex = set._indexes[value]; if (valueIndex != 0) { // Equivalent to contains(set, value) // To delete an element from the _values array in O(1), we swap the element to delete with the last one in // the array, and then remove the last element (sometimes called as 'swap and pop'). // This modifies the order of the array, as noted in {at}. uint256 toDeleteIndex = valueIndex - 1; uint256 lastIndex = set._values.length - 1; if (lastIndex != toDeleteIndex) { bytes32 lastValue = set._values[lastIndex]; // Move the last value to the index where the value to delete is set._values[toDeleteIndex] = lastValue; // Update the index for the moved value set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex } // Delete the slot where the moved value was stored set._values.pop(); // Delete the index for the deleted slot delete set._indexes[value]; return true; } else { return false; } } /** * @dev Returns true if the value is in the set. O(1). */ function _contains(Set storage set, bytes32 value) private view returns (bool) { return set._indexes[value] != 0; } /** * @dev Returns the number of values on the set. O(1). */ function _length(Set storage set) private view returns (uint256) { return set._values.length; } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function _at(Set storage set, uint256 index) private view returns (bytes32) { return set._values[index]; } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function _values(Set storage set) private view returns (bytes32[] memory) { return set._values; } // Bytes32Set struct Bytes32Set { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _add(set._inner, value); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _remove(set._inner, value); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { return _contains(set._inner, value); } /** * @dev Returns the number of values in the set. O(1). */ function length(Bytes32Set storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { return _at(set._inner, index); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { return _values(set._inner); } // AddressSet struct AddressSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(AddressSet storage set, address value) internal returns (bool) { return _add(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(AddressSet storage set, address value) internal returns (bool) { return _remove(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(AddressSet storage set, address value) internal view returns (bool) { return _contains(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns the number of values in the set. O(1). */ function length(AddressSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(AddressSet storage set, uint256 index) internal view returns (address) { return address(uint160(uint256(_at(set._inner, index)))); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(AddressSet storage set) internal view returns (address[] memory) { bytes32[] memory store = _values(set._inner); address[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } // UintSet struct UintSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(UintSet storage set, uint256 value) internal returns (bool) { return _add(set._inner, bytes32(value)); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(UintSet storage set, uint256 value) internal returns (bool) { return _remove(set._inner, bytes32(value)); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(UintSet storage set, uint256 value) internal view returns (bool) { return _contains(set._inner, bytes32(value)); } /** * @dev Returns the number of values on the set. O(1). */ function length(UintSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(UintSet storage set, uint256 index) internal view returns (uint256) { return uint256(_at(set._inner, index)); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(UintSet storage set) internal view returns (uint256[] memory) { bytes32[] memory store = _values(set._inner); uint256[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } } // SPDX-License-Identifier: MIT pragma solidity >=0.5.0; import "../ICommonOFT.sol"; /** * @dev Interface of the IOFT core standard */ interface IOFTWithFee is ICommonOFT { /** * @dev send `_amount` amount of token to (`_dstChainId`, `_toAddress`) from `_from` * `_from` the owner of token * `_dstChainId` the destination chain identifier * `_toAddress` can be any size depending on the `dstChainId`. * `_amount` the quantity of tokens in wei * `_minAmount` the minimum amount of tokens to receive on dstChain * `_refundAddress` the address LayerZero refunds if too much message fee is sent * `_zroPaymentAddress` set to address(0x0) if not paying in ZRO (LayerZero Token) * `_adapterParams` is a flexible bytes array to indicate messaging adapter services */ function sendFrom(address _from, uint16 _dstChainId, bytes32 _toAddress, uint _amount, uint _minAmount, LzCallParams calldata _callParams) external payable; function sendAndCall(address _from, uint16 _dstChainId, bytes32 _toAddress, uint _amount, uint _minAmount, bytes calldata _payload, uint64 _dstGasForCall, LzCallParams calldata _callParams) external payable; } // SPDX-License-Identifier: MIT pragma solidity =0.8.14; /* ░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗ ░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║ ░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║ ░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║ ░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║ ░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝ * * MIT License * =========== * * Copyright (c) 2020 WooTrade * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /// @title Woo private pool for swap. /// @notice Use this contract to directly interfact with woo's synthetic proactive /// marketing making pool. /// @author woo.network interface IWooPPV2 { /* ----- Events ----- */ event Deposit(address indexed token, address indexed sender, uint256 amount); event Withdraw(address indexed token, address indexed receiver, uint256 amount); event Migrate(address indexed token, address indexed receiver, uint256 amount); event AdminUpdated(address indexed addr, bool flag); event FeeAddrUpdated(address indexed newFeeAddr); event WooracleUpdated(address indexed newWooracle); event WooSwap( address indexed fromToken, address indexed toToken, uint256 fromAmount, uint256 toAmount, address from, address indexed to, address rebateTo, uint256 swapVol, uint256 swapFee ); /* ----- External Functions ----- */ /// @notice The quote token address (immutable). /// @return address of quote token function quoteToken() external view returns (address); /// @notice Gets the pool size of the specified token (swap liquidity). /// @param token the token address /// @return the pool size function poolSize(address token) external view returns (uint256); /// @notice Query the amount to swap `fromToken` to `toToken`, without checking the pool reserve balance. /// @param fromToken the from token /// @param toToken the to token /// @param fromAmount the amount of `fromToken` to swap /// @return toAmount the swapped amount of `toToken` function tryQuery( address fromToken, address toToken, uint256 fromAmount ) external view returns (uint256 toAmount); /// @notice Query the amount to swap `fromToken` to `toToken`, with checking the pool reserve balance. /// @dev tx reverts when 'toToken' balance is insufficient. /// @param fromToken the from token /// @param toToken the to token /// @param fromAmount the amount of `fromToken` to swap /// @return toAmount the swapped amount of `toToken` function query( address fromToken, address toToken, uint256 fromAmount ) external view returns (uint256 toAmount); /// @notice Swap `fromToken` to `toToken`. /// @param fromToken the from token /// @param toToken the to token /// @param fromAmount the amount of `fromToken` to swap /// @param minToAmount the minimum amount of `toToken` to receive /// @param to the destination address /// @param rebateTo the rebate address (optional, can be address ZERO) /// @return realToAmount the amount of toToken to receive function swap( address fromToken, address toToken, uint256 fromAmount, uint256 minToAmount, address to, address rebateTo ) external returns (uint256 realToAmount); /// @notice Deposit the specified token into the liquidity pool of WooPPV2. /// @param token the token to deposit /// @param amount the deposit amount function deposit(address token, uint256 amount) external; } // SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^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 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. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } } // SPDX-License-Identifier: MIT pragma solidity >=0.5.0; import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; /** * @dev Interface of the IOFT core standard */ interface ICommonOFT is IERC165 { struct LzCallParams { address payable refundAddress; address zroPaymentAddress; bytes adapterParams; } /** * @dev estimate send token `_tokenId` to (`_dstChainId`, `_toAddress`) * _dstChainId - L0 defined chain id to send tokens too * _toAddress - dynamic bytes array which contains the address to whom you are sending tokens to on the dstChain * _amount - amount of the tokens to transfer * _useZro - indicates to use zro to pay L0 fees * _adapterParam - flexible bytes array to indicate messaging adapter services in L0 */ function estimateSendFee(uint16 _dstChainId, bytes32 _toAddress, uint _amount, bool _useZro, bytes calldata _adapterParams) external view returns (uint nativeFee, uint zroFee); function estimateSendAndCallFee(uint16 _dstChainId, bytes32 _toAddress, uint _amount, bytes calldata _payload, uint64 _dstGasForCall, bool _useZro, bytes calldata _adapterParams) external view returns (uint nativeFee, uint zroFee); /** * @dev returns the circulating amount of tokens on current chain */ function circulatingSupply() external view returns (uint); /** * @dev returns the address of the ERC20 token */ function token() external view returns (address); } // SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }