编译器
0.8.21+commit.d9974bed
文件 1 的 15:Address.sol
pragma solidity ^0.8.1;
library Address {
function isContract(address account) internal view returns (bool) {
return account.code.length > 0;
}
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
文件 2 的 15:ButterRouterV2.sol
pragma solidity 0.8.21;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable2Step.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@butternetwork/bridge/contracts/interface/IButterMosV2.sol";
import "@butternetwork/bridge/contracts/interface/IButterReceiver.sol";
import "./lib/ErrorMessage.sol";
import "./abstract/Router.sol";
import "./lib/Helper.sol";
contract ButterRouterV2 is Router, ReentrancyGuard, IButterReceiver {
using SafeERC20 for IERC20;
using Address for address;
address public mosAddress;
uint256 public gasForReFund = 80000;
struct BridgeParam {
uint256 toChain;
bytes receiver;
bytes data;
}
event SetMos(address indexed mos);
event SetGasForReFund(uint256 indexed _gasForReFund);
event SwapAndBridge(
bytes32 indexed orderId,
address indexed from,
address indexed originToken,
address bridgeToken,
uint256 originAmount,
uint256 bridgeAmount,
uint256 fromChain,
uint256 toChain,
bytes to
);
event RemoteSwapAndCall(
bytes32 indexed orderId,
address indexed receiver,
address indexed target,
address originToken,
address swapToken,
uint256 originAmount,
uint256 swapAmount,
uint256 callAmount,
uint256 fromChain,
uint256 toChain,
bytes from
);
constructor(address _mosAddress, address _owner, address _wToken) payable Router(_owner, _wToken) {
_setMosAddress(_mosAddress);
}
function swapAndBridge(
address _srcToken,
uint256 _amount,
bytes calldata _swapData,
bytes calldata _bridgeData,
bytes calldata _permitData
) external payable nonReentrant transferIn(_srcToken, _amount, _permitData) {
require(_swapData.length + _bridgeData.length > 0, ErrorMessage.DATA_EMPTY);
SwapTemp memory swapTemp;
swapTemp.srcToken = _srcToken;
swapTemp.srcAmount = _amount;
swapTemp.swapToken = _srcToken;
swapTemp.swapAmount = _amount;
bytes memory receiver;
if (_swapData.length > 0) {
Helper.SwapParam memory swap = abi.decode(_swapData, (Helper.SwapParam));
bool result;
(result, swapTemp.swapToken, swapTemp.swapAmount) = _makeSwap(swapTemp.srcAmount, swapTemp.srcToken, swap);
require(result, ErrorMessage.SWAP_FAIL);
require(swapTemp.swapAmount >= swap.minReturnAmount, ErrorMessage.RECEIVE_LOW);
if (_bridgeData.length == 0 && swapTemp.swapAmount > 0) {
receiver = abi.encodePacked(swap.receiver);
Helper._transfer(swapTemp.swapToken, swap.receiver, swapTemp.swapAmount);
}
}
bytes32 orderId;
if (_bridgeData.length > 0) {
BridgeParam memory bridge = abi.decode(_bridgeData, (BridgeParam));
swapTemp.toChain = bridge.toChain;
receiver = bridge.receiver;
orderId = _doBridge(msg.sender, swapTemp.swapToken, swapTemp.swapAmount, bridge);
}
emit SwapAndBridge(
orderId,
msg.sender,
swapTemp.srcToken,
swapTemp.swapToken,
swapTemp.srcAmount,
swapTemp.swapAmount,
block.chainid,
swapTemp.toChain,
receiver
);
}
function swapAndCall(
bytes32 _transferId,
address _srcToken,
uint256 _amount,
FeeType _feeType,
bytes calldata _swapData,
bytes calldata _callbackData,
bytes calldata _permitData
) external payable nonReentrant transferIn(_srcToken, _amount, _permitData) {
SwapTemp memory swapTemp;
swapTemp.srcToken = _srcToken;
swapTemp.srcAmount = _amount;
swapTemp.transferId = _transferId;
swapTemp.feeType = _feeType;
require(_swapData.length + _callbackData.length > 0, ErrorMessage.DATA_EMPTY);
(, swapTemp.swapAmount) = _collectFee(
swapTemp.srcToken,
swapTemp.srcAmount,
swapTemp.transferId,
swapTemp.feeType
);
(
swapTemp.receiver,
swapTemp.target,
swapTemp.swapToken,
swapTemp.swapAmount,
swapTemp.callAmount
) = _doSwapAndCall(_swapData, _callbackData, swapTemp.srcToken, swapTemp.swapAmount);
if (swapTemp.swapAmount > swapTemp.callAmount) {
Helper._transfer(swapTemp.swapToken, swapTemp.receiver, (swapTemp.swapAmount - swapTemp.callAmount));
}
emit SwapAndCall(
msg.sender,
swapTemp.receiver,
swapTemp.target,
swapTemp.transferId,
swapTemp.srcToken,
swapTemp.swapToken,
swapTemp.srcAmount,
swapTemp.swapAmount,
swapTemp.callAmount
);
}
function onReceived(
bytes32 _orderId,
address _srcToken,
uint256 _amount,
uint256 _fromChain,
bytes calldata _from,
bytes calldata _swapAndCall
) external nonReentrant {
SwapTemp memory swapTemp;
swapTemp.srcToken = _srcToken;
swapTemp.srcAmount = _amount;
swapTemp.swapToken = _srcToken;
swapTemp.swapAmount = _amount;
swapTemp.fromChain = _fromChain;
swapTemp.toChain = block.chainid;
swapTemp.from = _from;
nativeBalanceBeforeExec = address(this).balance;
require(msg.sender == mosAddress, ErrorMessage.MOS_ONLY);
require(Helper._getBalance(swapTemp.srcToken, address(this)) >= _amount, ErrorMessage.RECEIVE_LOW);
(bytes memory _swapData, bytes memory _callbackData) = abi.decode(_swapAndCall, (bytes, bytes));
require(_swapData.length + _callbackData.length > 0, ErrorMessage.DATA_EMPTY);
bool result = true;
uint256 minExecGas = gasForReFund * 2;
if (_swapData.length > 0) {
Helper.SwapParam memory swap = abi.decode(_swapData, (Helper.SwapParam));
swapTemp.receiver = swap.receiver;
if (gasleft() > minExecGas) {
try
this.doRemoteSwap{gas: gasleft() - gasForReFund}(swap, swapTemp.srcToken, swapTemp.srcAmount)
returns (address target, address dstToken, uint256 dstAmount) {
swapTemp.swapToken = dstToken;
swapTemp.target = target;
swapTemp.swapAmount = dstAmount;
} catch {
result = false;
}
}
}
if (_callbackData.length > 0) {
Helper.CallbackParam memory callParam = abi.decode(_callbackData, (Helper.CallbackParam));
if (swapTemp.receiver == address(0)) {
swapTemp.receiver = callParam.receiver;
}
if (result && gasleft() > minExecGas) {
try
this.doRemoteCall{gas: gasleft() - gasForReFund}(callParam, swapTemp.swapToken, swapTemp.swapAmount)
returns (address target, uint256 callAmount) {
swapTemp.target = target;
swapTemp.callAmount = callAmount;
swapTemp.receiver = callParam.receiver;
} catch {}
}
}
if (swapTemp.swapAmount > swapTemp.callAmount) {
Helper._transfer(swapTemp.swapToken, swapTemp.receiver, (swapTemp.swapAmount - swapTemp.callAmount));
}
emit RemoteSwapAndCall(
_orderId,
swapTemp.receiver,
swapTemp.target,
swapTemp.srcToken,
swapTemp.swapToken,
swapTemp.srcAmount,
swapTemp.swapAmount,
swapTemp.callAmount,
swapTemp.fromChain,
swapTemp.toChain,
swapTemp.from
);
}
function doRemoteSwap(
Helper.SwapParam memory _swap,
address _srcToken,
uint256 _amount
) external returns (address target, address dstToken, uint256 dstAmount) {
require(msg.sender == address(this));
bool result;
(result, dstToken, dstAmount) = _makeSwap(_amount, _srcToken, _swap);
require(result, ErrorMessage.SWAP_FAIL);
require(dstAmount >= _swap.minReturnAmount, ErrorMessage.RECEIVE_LOW);
target = _swap.executor;
}
function doRemoteCall(
Helper.CallbackParam memory _callParam,
address _callToken,
uint256 _amount
) external returns (address target, uint256 callAmount) {
require(msg.sender == address(this));
bool result;
(result, callAmount) = _callBack(_amount, _callToken, _callParam);
require(result, ErrorMessage.CALL_FAIL);
target = _callParam.target;
}
function _doBridge(
address _sender,
address _token,
uint256 _value,
BridgeParam memory _bridge
) internal returns (bytes32 _orderId) {
if (Helper._isNative(_token)) {
_orderId = IButterMosV2(mosAddress).swapOutNative{value: _value}(
_sender,
_bridge.receiver,
_bridge.toChain,
_bridge.data
);
} else {
IERC20(_token).safeApprove(mosAddress, _value);
_orderId = IButterMosV2(mosAddress).swapOutToken(
_sender,
_token,
_bridge.receiver,
_value,
_bridge.toChain,
_bridge.data
);
}
}
function setGasForReFund(uint256 _gasForReFund) external onlyOwner {
gasForReFund = _gasForReFund;
emit SetGasForReFund(_gasForReFund);
}
function setMosAddress(address _mosAddress) public onlyOwner returns (bool) {
_setMosAddress(_mosAddress);
return true;
}
function _setMosAddress(address _mosAddress) internal returns (bool) {
require(_mosAddress.isContract(), ErrorMessage.NOT_CONTRACT);
mosAddress = _mosAddress;
emit SetMos(_mosAddress);
return true;
}
receive() external payable {}
}
文件 3 的 15:Context.sol
pragma solidity ^0.8.0;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
文件 4 的 15:ErrorMessage.sol
pragma solidity 0.8.21;
library ErrorMessage {
string internal constant ZERO_IN = "ButterRouterV2: zero in";
string internal constant FEE_MISMATCH = "ButterRouterV2: fee mismatch";
string internal constant FEE_LOWER = "ButterRouterV2: lower than fee";
string internal constant ZERO_ADDR = "ButterRouterV2: zero addr";
string internal constant NOT_CONTRACT = "ButterRouterV2: not contract";
string internal constant BRIDGE_REQUIRE = "ButterRouterV2: bridge data required";
string internal constant RECEIVE_LOW = "ButterRouterV2: receive too low";
string internal constant SWAP_FAIL = "ButterRouterV2: swap failed";
string internal constant SWAP_REQUIRE = "ButterRouterV2: swap data required";
string internal constant CALL_AMOUNT_INVALID = "ButterRouterV2: callback amount invalid";
string internal constant CALL_FAIL = "ButterRouterV2: callback failed";
string internal constant MOS_ONLY = "ButterRouterV2: mos only";
string internal constant DATA_EMPTY = "ButterRouterV2: data empty";
string internal constant NO_APPROVE = "ButterRouterV2: not approved";
string internal constant NATIVE_VALUE_OVERSPEND = "ButterRouterV2: native value overspend";
}
文件 5 的 15:Helper.sol
pragma solidity 0.8.21;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol";
library Helper {
using SafeERC20 for IERC20;
address internal constant ZERO_ADDRESS = address(0);
address internal constant NATIVE_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
struct CallbackParam {
address target;
address approveTo;
uint256 offset;
uint256 extraNativeAmount;
address receiver;
bytes data;
}
struct SwapParam {
uint8 dexType;
address executor;
address approveTo;
address receiver;
address dstToken;
uint256 minReturnAmount;
bytes data;
}
function _isNative(address token) internal pure returns (bool) {
return (token == ZERO_ADDRESS || token == NATIVE_ADDRESS);
}
function _getBalance(address _token, address _account) internal view returns (uint256) {
if (_isNative(_token)) {
return _account.balance;
} else {
return IERC20(_token).balanceOf(_account);
}
}
function _transfer(address _token, address _to, uint256 _amount) internal {
if (_isNative(_token)) {
Address.sendValue(payable(_to), _amount);
} else {
IERC20(_token).safeTransfer(_to, _amount);
}
}
function _safeWithdraw(address _wToken, uint _value) internal returns (bool) {
(bool success, bytes memory data) = _wToken.call(abi.encodeWithSelector(0x2e1a7d4d, _value));
return (success && (data.length == 0 || abi.decode(data, (bool))));
}
function _getFirst4Bytes(bytes memory data) internal pure returns (bytes4 outBytes4) {
if (data.length == 0) {
return 0x0;
}
assembly {
outBytes4 := mload(add(data, 32))
}
}
function _makeSwap(
uint256 _amount,
address _srcToken,
SwapParam memory _swap
) internal returns (bool _result, address _dstToken, uint256 _returnAmount) {
_dstToken = _swap.dstToken;
uint256 nativeValue = 0;
bool isNative = Helper._isNative(_srcToken);
if (isNative) {
nativeValue = _amount;
} else {
IERC20(_srcToken).safeApprove(_swap.approveTo, 0);
IERC20(_srcToken).safeApprove(_swap.approveTo, _amount);
}
_returnAmount = Helper._getBalance(_dstToken, address(this));
(_result, ) = _swap.executor.call{value: nativeValue}(_swap.data);
_returnAmount = Helper._getBalance(_dstToken, address(this)) - _returnAmount;
if (!isNative) {
IERC20(_srcToken).safeApprove(_swap.approveTo, 0);
}
}
function _callBack(
uint256 _amount,
address _token,
CallbackParam memory _callParam
) internal returns (bool _result, uint256 _callAmount) {
_callAmount = Helper._getBalance(_token, address(this));
uint256 offset = _callParam.offset;
bytes memory callDatas = _callParam.data;
if (offset != 0) {
assembly {
mstore(add(callDatas, offset), _amount)
}
}
if (Helper._isNative(_token)) {
(_result, ) = _callParam.target.call{value: _amount}(callDatas);
} else {
if (_amount != 0) IERC20(_token).safeIncreaseAllowance(_callParam.approveTo, _amount);
(_result, ) = _callParam.target.call{value: _callParam.extraNativeAmount}(callDatas);
if (_amount != 0) IERC20(_token).safeApprove(_callParam.approveTo, 0);
}
_callAmount = _callAmount - Helper._getBalance(_token, address(this));
}
function _permit(bytes memory _data) internal {
(
address token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) = abi.decode(_data, (address, address, address, uint256, uint256, uint8, bytes32, bytes32));
SafeERC20.safePermit(IERC20Permit(token), owner, spender, value, deadline, v, r, s);
}
}
文件 6 的 15:IButterMosV2.sol
pragma solidity ^0.8.0;
interface IButterMosV2 {
function swapOutToken(
address _sender,
address _token,
bytes memory _to,
uint256 _amount,
uint256 _toChain,
bytes calldata _swapData
) external returns (bytes32 orderId);
function swapOutNative(
address _sender,
bytes memory _to,
uint256 _toChain,
bytes calldata _swapData
) external payable returns (bytes32 orderId);
function depositToken(address _token, address to, uint256 _amount) external;
function depositNative(address _to) external payable;
event SetButterRouterAddress(address indexed _newRouter);
event mapTransferOut(
uint256 indexed fromChain,
uint256 indexed toChain,
bytes32 orderId,
bytes token,
bytes from,
bytes to,
uint256 amount,
bytes toChainToken
);
event mapDepositOut(
uint256 indexed fromChain,
uint256 indexed toChain,
bytes32 orderId,
address token,
bytes from,
address to,
uint256 amount
);
event mapSwapOut(
uint256 indexed fromChain,
uint256 indexed toChain,
bytes32 orderId,
bytes token,
bytes from,
bytes to,
uint256 amount,
bytes swapData
);
event mapSwapIn(
uint256 indexed fromChain,
uint256 indexed toChain,
bytes32 indexed orderId,
address token,
bytes from,
address toAddress,
uint256 amountOut
);
}
文件 7 的 15:IButterReceiver.sol
pragma solidity ^0.8.0;
interface IButterReceiver {
function onReceived(
bytes32 _orderId,
address _srcToken,
uint256 _amount,
uint256 _fromChain,
bytes calldata _from,
bytes calldata _payload
) external;
}
文件 8 的 15:IERC20.sol
pragma solidity ^0.8.0;
interface IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
文件 9 的 15:IERC20Permit.sol
pragma solidity ^0.8.0;
interface IERC20Permit {
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
function nonces(address owner) external view returns (uint256);
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
文件 10 的 15:Ownable.sol
pragma solidity ^0.8.0;
import "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor() {
_transferOwnership(_msgSender());
}
modifier onlyOwner() {
_checkOwner();
_;
}
function owner() public view virtual returns (address) {
return _owner;
}
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
文件 11 的 15:Ownable2Step.sol
pragma solidity ^0.8.0;
import "./Ownable.sol";
abstract contract Ownable2Step is Ownable {
address private _pendingOwner;
event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
function pendingOwner() public view virtual returns (address) {
return _pendingOwner;
}
function transferOwnership(address newOwner) public virtual override onlyOwner {
_pendingOwner = newOwner;
emit OwnershipTransferStarted(owner(), newOwner);
}
function _transferOwnership(address newOwner) internal virtual override {
delete _pendingOwner;
super._transferOwnership(newOwner);
}
function acceptOwnership() public virtual {
address sender = _msgSender();
require(pendingOwner() == sender, "Ownable2Step: caller is not the new owner");
_transferOwnership(sender);
}
}
文件 12 的 15:ReentrancyGuard.sol
pragma solidity ^0.8.0;
abstract contract ReentrancyGuard {
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
_status = _ENTERED;
}
function _nonReentrantAfter() private {
_status = _NOT_ENTERED;
}
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == _ENTERED;
}
}
文件 13 的 15:Router.sol
pragma solidity 0.8.21;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable2Step.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "../lib/ErrorMessage.sol";
import "../lib/Helper.sol";
abstract contract Router is Ownable2Step {
using SafeERC20 for IERC20;
using Address for address;
uint256 public feeRate;
uint256 public fixedFee;
address public feeReceiver;
address internal immutable wToken;
uint256 internal nativeBalanceBeforeExec;
uint256 private constant FEE_DENOMINATOR = 1000000;
mapping(address => bool) public approved;
event Approve(address indexed executor, bool indexed flag);
event SetFee(address indexed receiver, uint256 indexed rate, uint256 indexed fixedf);
event CollectFee(
address indexed token,
address indexed receiver,
uint256 indexed amount,
bytes32 transferId,
FeeType feeType
);
enum FeeType {
FIXED,
PROPORTION
}
struct SwapTemp {
address srcToken;
address swapToken;
uint256 srcAmount;
uint256 swapAmount;
bytes32 transferId;
address receiver;
address target;
uint256 callAmount;
uint256 fromChain;
uint256 toChain;
bytes from;
FeeType feeType;
}
event SwapAndCall(
address indexed from,
address indexed receiver,
address indexed target,
bytes32 transferId,
address originToken,
address swapToken,
uint256 originAmount,
uint256 swapAmount,
uint256 callAmount
);
modifier transferIn(
address token,
uint256 amount,
bytes memory permitData
) {
require(amount > 0, ErrorMessage.ZERO_IN);
if (permitData.length > 0) {
Helper._permit(permitData);
}
nativeBalanceBeforeExec = address(this).balance - msg.value;
if (Helper._isNative(token)) {
require(msg.value >= amount, ErrorMessage.FEE_MISMATCH);
} else {
SafeERC20.safeTransferFrom(IERC20(token), msg.sender, address(this), amount);
}
_;
nativeBalanceBeforeExec = 0;
}
constructor(address _owner, address _wToken) payable {
require(_owner != Helper.ZERO_ADDRESS, ErrorMessage.ZERO_ADDR);
require(_wToken.isContract(), ErrorMessage.NOT_CONTRACT);
wToken = _wToken;
_transferOwnership(_owner);
}
function _doSwapAndCall(
bytes memory _swapData,
bytes memory _callbackData,
address _srcToken,
uint256 _amount
) internal returns (address receiver, address target, address dstToken, uint256 swapOutAmount, uint256 callAmount) {
bool result;
swapOutAmount = _amount;
dstToken = _srcToken;
if (_swapData.length > 0) {
Helper.SwapParam memory swap = abi.decode(_swapData, (Helper.SwapParam));
(result, dstToken, swapOutAmount) = _makeSwap(_amount, _srcToken, swap);
require(result, ErrorMessage.SWAP_FAIL);
require(swapOutAmount >= swap.minReturnAmount, ErrorMessage.RECEIVE_LOW);
receiver = swap.receiver;
target = swap.executor;
}
if (_callbackData.length > 0) {
Helper.CallbackParam memory callParam = abi.decode(_callbackData, (Helper.CallbackParam));
(result, callAmount) = _callBack(swapOutAmount, dstToken, callParam);
require(result, ErrorMessage.CALL_FAIL);
receiver = callParam.receiver;
target = callParam.target;
}
}
function setFee(address _feeReceiver, uint256 _feeRate, uint256 _fixedFee) external onlyOwner {
require(_feeReceiver != Helper.ZERO_ADDRESS, ErrorMessage.ZERO_ADDR);
require(_feeRate < FEE_DENOMINATOR);
feeReceiver = _feeReceiver;
feeRate = _feeRate;
fixedFee = _fixedFee;
emit SetFee(_feeReceiver, _feeRate, fixedFee);
}
function getFee(
uint256 _amount,
address _token,
FeeType _feeType
) external view returns (address _feeReceiver, address _feeToken, uint256 _fee, uint256 _feeAfter) {
if (feeReceiver == Helper.ZERO_ADDRESS) {
return (Helper.ZERO_ADDRESS, Helper.ZERO_ADDRESS, 0, _amount);
}
if (_feeType == FeeType.FIXED) {
_feeToken = Helper.ZERO_ADDRESS;
_fee = fixedFee;
if (!Helper._isNative(_token)) {
_feeAfter = _amount;
} else {
_feeAfter = _amount - _fee;
}
} else {
_feeToken = _token;
_fee = (_amount * feeRate) / FEE_DENOMINATOR;
_feeAfter = _amount - _fee;
}
_feeReceiver = feeReceiver;
}
function getInputBeforeFee(
uint256 _amountAfterFee,
address _token,
FeeType _feeType
) external view returns (uint256 _input, address _feeReceiver, address _feeToken, uint256 _fee) {
if (feeReceiver == Helper.ZERO_ADDRESS) {
return (_amountAfterFee, Helper.ZERO_ADDRESS, Helper.ZERO_ADDRESS, 0);
}
if (_feeType == FeeType.FIXED) {
_feeToken = Helper.ZERO_ADDRESS;
_fee = fixedFee;
if (!Helper._isNative(_token)) {
_input = _amountAfterFee;
} else {
_input = _amountAfterFee + _fee;
}
} else {
_feeToken = _token;
_input = (_amountAfterFee * FEE_DENOMINATOR) / (FEE_DENOMINATOR - feeRate) + 1;
_fee = _input - _amountAfterFee;
}
_feeReceiver = feeReceiver;
}
function _collectFee(
address _token,
uint256 _amount,
bytes32 transferId,
FeeType _feeType
) internal returns (uint256 _fee, uint256 _remain) {
if (feeReceiver == Helper.ZERO_ADDRESS) {
_remain = _amount;
return (_fee, _remain);
}
if (_feeType == FeeType.FIXED) {
_fee = fixedFee;
if (Helper._isNative(_token)) {
require(msg.value > fixedFee, ErrorMessage.FEE_LOWER);
_remain = _amount - _fee;
} else {
require(msg.value >= fixedFee, ErrorMessage.FEE_MISMATCH);
_remain = _amount;
}
_token = Helper.NATIVE_ADDRESS;
} else {
_fee = (_amount * feeRate) / FEE_DENOMINATOR;
_remain = _amount - _fee;
}
if (_fee > 0) {
Helper._transfer(_token, feeReceiver, _fee);
emit CollectFee(_token, feeReceiver, _fee, transferId, _feeType);
}
}
function _callBack(
uint256 _amount,
address _token,
Helper.CallbackParam memory _callParam
) internal returns (bool _result, uint256 _callAmount) {
require(approved[_callParam.target], ErrorMessage.NO_APPROVE);
(_result, _callAmount) = Helper._callBack(_amount, _token, _callParam);
require(address(this).balance >= nativeBalanceBeforeExec, ErrorMessage.NATIVE_VALUE_OVERSPEND);
}
function _makeSwap(
uint256 _amount,
address _srcToken,
Helper.SwapParam memory _swap
) internal returns (bool _result, address _dstToken, uint256 _returnAmount) {
require(approved[_swap.executor] || _swap.executor == wToken, ErrorMessage.NO_APPROVE);
if (_swap.executor == wToken) {
bytes4 sig = Helper._getFirst4Bytes(_swap.data);
if (sig != bytes4(0x2e1a7d4d) && sig != bytes4(0xd0e30db0)) {
return (false, _srcToken, 0);
}
}
(_result, _dstToken, _returnAmount) = Helper._makeSwap(_amount, _srcToken, _swap);
}
function setAuthorization(address[] calldata _executors, bool _flag) external onlyOwner {
require(_executors.length > 0, ErrorMessage.DATA_EMPTY);
for (uint i = 0; i < _executors.length; i++) {
require(_executors[i].isContract(), ErrorMessage.NOT_CONTRACT);
approved[_executors[i]] = _flag;
emit Approve(_executors[i], _flag);
}
}
function rescueFunds(address _token, uint256 _amount) external onlyOwner {
Helper._transfer(_token, msg.sender, _amount);
}
}
文件 14 的 15:SafeERC20.sol
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";
library SafeERC20 {
using Address for address;
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
function safeApprove(IERC20 token, address spender, uint256 value) internal {
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
function _callOptionalReturn(IERC20 token, bytes memory data) private {
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
(bool success, bytes memory returndata) = address(token).call(data);
return
success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
}
}
文件 15 的 15:draft-IERC20Permit.sol
pragma solidity ^0.8.0;
import "./IERC20Permit.sol";
{
"compilationTarget": {
"contracts/ButterRouterV2.sol": "ButterRouterV2"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_mosAddress","type":"address"},{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_wToken","type":"address"}],"stateMutability":"payable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"executor","type":"address"},{"indexed":true,"internalType":"bool","name":"flag","type":"bool"}],"name":"Approve","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"transferId","type":"bytes32"},{"indexed":false,"internalType":"enum Router.FeeType","name":"feeType","type":"uint8"}],"name":"CollectFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"orderId","type":"bytes32"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":true,"internalType":"address","name":"target","type":"address"},{"indexed":false,"internalType":"address","name":"originToken","type":"address"},{"indexed":false,"internalType":"address","name":"swapToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"originAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"swapAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"callAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fromChain","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"toChain","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"from","type":"bytes"}],"name":"RemoteSwapAndCall","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":true,"internalType":"uint256","name":"rate","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"fixedf","type":"uint256"}],"name":"SetFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_gasForReFund","type":"uint256"}],"name":"SetGasForReFund","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"mos","type":"address"}],"name":"SetMos","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"orderId","type":"bytes32"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"originToken","type":"address"},{"indexed":false,"internalType":"address","name":"bridgeToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"originAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"bridgeAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fromChain","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"toChain","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"to","type":"bytes"}],"name":"SwapAndBridge","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":true,"internalType":"address","name":"target","type":"address"},{"indexed":false,"internalType":"bytes32","name":"transferId","type":"bytes32"},{"indexed":false,"internalType":"address","name":"originToken","type":"address"},{"indexed":false,"internalType":"address","name":"swapToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"originAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"swapAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"callAmount","type":"uint256"}],"name":"SwapAndCall","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"approved","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"address","name":"approveTo","type":"address"},{"internalType":"uint256","name":"offset","type":"uint256"},{"internalType":"uint256","name":"extraNativeAmount","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct Helper.CallbackParam","name":"_callParam","type":"tuple"},{"internalType":"address","name":"_callToken","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"doRemoteCall","outputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"callAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint8","name":"dexType","type":"uint8"},{"internalType":"address","name":"executor","type":"address"},{"internalType":"address","name":"approveTo","type":"address"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"dstToken","type":"address"},{"internalType":"uint256","name":"minReturnAmount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct Helper.SwapParam","name":"_swap","type":"tuple"},{"internalType":"address","name":"_srcToken","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"doRemoteSwap","outputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"address","name":"dstToken","type":"address"},{"internalType":"uint256","name":"dstAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"feeRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeReceiver","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fixedFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gasForReFund","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_token","type":"address"},{"internalType":"enum Router.FeeType","name":"_feeType","type":"uint8"}],"name":"getFee","outputs":[{"internalType":"address","name":"_feeReceiver","type":"address"},{"internalType":"address","name":"_feeToken","type":"address"},{"internalType":"uint256","name":"_fee","type":"uint256"},{"internalType":"uint256","name":"_feeAfter","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amountAfterFee","type":"uint256"},{"internalType":"address","name":"_token","type":"address"},{"internalType":"enum Router.FeeType","name":"_feeType","type":"uint8"}],"name":"getInputBeforeFee","outputs":[{"internalType":"uint256","name":"_input","type":"uint256"},{"internalType":"address","name":"_feeReceiver","type":"address"},{"internalType":"address","name":"_feeToken","type":"address"},{"internalType":"uint256","name":"_fee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mosAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_orderId","type":"bytes32"},{"internalType":"address","name":"_srcToken","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_fromChain","type":"uint256"},{"internalType":"bytes","name":"_from","type":"bytes"},{"internalType":"bytes","name":"_swapAndCall","type":"bytes"}],"name":"onReceived","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"rescueFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_executors","type":"address[]"},{"internalType":"bool","name":"_flag","type":"bool"}],"name":"setAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_feeReceiver","type":"address"},{"internalType":"uint256","name":"_feeRate","type":"uint256"},{"internalType":"uint256","name":"_fixedFee","type":"uint256"}],"name":"setFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_gasForReFund","type":"uint256"}],"name":"setGasForReFund","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_mosAddress","type":"address"}],"name":"setMosAddress","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_srcToken","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"bytes","name":"_swapData","type":"bytes"},{"internalType":"bytes","name":"_bridgeData","type":"bytes"},{"internalType":"bytes","name":"_permitData","type":"bytes"}],"name":"swapAndBridge","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_transferId","type":"bytes32"},{"internalType":"address","name":"_srcToken","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"enum Router.FeeType","name":"_feeType","type":"uint8"},{"internalType":"bytes","name":"_swapData","type":"bytes"},{"internalType":"bytes","name":"_callbackData","type":"bytes"},{"internalType":"bytes","name":"_permitData","type":"bytes"}],"name":"swapAndCall","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]