编译器
0.8.16+commit.07a7930e
文件 1 的 6: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 functionCall(target, data, "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");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResult(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) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResult(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) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResult(success, returndata, errorMessage);
}
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
文件 2 的 6: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);
}
文件 3 的 6:Ownable.sol
pragma solidity 0.8.16;
abstract contract Ownable {
error Unauthorized();
error ZeroAddress();
event OwnerSet(address indexed newOwner_);
event PendingOwnerSet(address indexed pendingOwner_);
address public owner;
address public pendingOwner;
modifier onlyOwner() {
if (msg.sender != owner) revert Unauthorized();
_;
}
function setPendingOwner(address pendingOwner_) external onlyOwner {
_setPendingOwner(pendingOwner_);
}
function acceptOwnership() external {
if (msg.sender != pendingOwner) revert Unauthorized();
_setPendingOwner(address(0));
_setOwner(msg.sender);
}
function _setOwner(address owner_) internal {
if (owner_ == address(0)) revert ZeroAddress();
emit OwnerSet(owner = owner_);
}
function _setPendingOwner(address pendingOwner_) internal {
emit PendingOwnerSet(pendingOwner = pendingOwner_);
}
}
文件 4 的 6:SafeERC20.sol
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/draft-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 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
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");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
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");
if (returndata.length > 0) {
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
文件 5 的 6:SwapFeeRouter.sol
pragma solidity 0.8.16;
import { IERC20, SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { Ownable } from "./Ownable.sol";
contract SwapFeeRouter is Ownable {
error ETHTransferFailed(bytes errorData);
error FeeBasisPointsNotRespected(uint256 expectedFeeBasisPoints_, uint256 actualFeeBasisPoints_);
error ContractNotWhitelisted(address callee);
error RenterAttempted();
error SwapCallFailed(bytes errorData);
event ContractAddedToWhitelist(address indexed contract_);
event ContractRemovedFromWhitelist(address indexed contract_);
event ETHPulled(address indexed destination_, uint256 amount_);
event FeeSet(uint256 feeBasisPoints_);
event TokensPulled(address indexed token_, address indexed destination_, uint256 amount_);
uint256 internal _locked = 1;
uint256 public feeBasisPoints;
mapping(address => bool) public isWhitelisted;
constructor(address owner_, uint256 feeBasisPoints_, address[] memory whitelist_) {
_setOwner(owner_);
_setFees(feeBasisPoints_);
_addToWhitelist(whitelist_);
}
modifier noRenter() {
if (_locked == 2) revert RenterAttempted();
_locked = 2;
_;
_locked = 1;
}
modifier feeBasisPointsRespected(uint256 feeBasisPoints_) {
if (feeBasisPoints_ < feeBasisPoints) revert FeeBasisPointsNotRespected(feeBasisPoints_, feeBasisPoints);
_;
}
function swapWithFeesOnInput(
address inAsset_,
uint256 swapAmount_,
uint256 feeBasisPoints_,
address swapContract_,
address tokenPuller_,
bytes calldata swapCallData_
) public noRenter feeBasisPointsRespected(feeBasisPoints_) {
SafeERC20.safeTransferFrom(IERC20(inAsset_), msg.sender, address(this), getAmountWithFees(swapAmount_, feeBasisPoints_));
_performSwap(inAsset_, swapAmount_, swapContract_, tokenPuller_, swapCallData_);
}
function swapWithFeesOnOutput(
address inAsset_,
uint256 swapAmount_,
address outAsset_,
uint256 feeBasisPoints_,
address swapContract_,
address tokenPuller_,
bytes calldata swapCallData_
) external noRenter feeBasisPointsRespected(feeBasisPoints_) {
uint256 startingOutAssetBalance = IERC20(outAsset_).balanceOf(address(this));
SafeERC20.safeTransferFrom(IERC20(inAsset_), msg.sender, address(this), swapAmount_);
_performSwap(inAsset_, swapAmount_, swapContract_, tokenPuller_, swapCallData_);
SafeERC20.safeTransfer(
IERC20(outAsset_),
msg.sender,
getAmountWithoutFees(
IERC20(outAsset_).balanceOf(address(this)) - startingOutAssetBalance,
feeBasisPoints
)
);
}
function swapFromEthWithFeesOnInput(
uint256 feeBasisPoints_,
address swapContract_,
bytes calldata swapCallData_
) external payable noRenter feeBasisPointsRespected(feeBasisPoints_) {
_performSwap(getAmountWithoutFees(msg.value, feeBasisPoints_), swapContract_, swapCallData_);
}
function swapFromEthWithFeesOnOutput(
address outAsset_,
uint256 feeBasisPoints_,
address swapContract_,
bytes calldata swapCallData_
) external payable noRenter feeBasisPointsRespected(feeBasisPoints_) {
uint256 startingOutAssetBalance = IERC20(outAsset_).balanceOf(address(this));
_performSwap(msg.value, swapContract_, swapCallData_);
SafeERC20.safeTransfer(
IERC20(outAsset_),
msg.sender,
getAmountWithoutFees(
IERC20(outAsset_).balanceOf(address(this)) - startingOutAssetBalance,
feeBasisPoints
)
);
}
function swapToEthWithFeesOnInput(
address inAsset_,
uint256 swapAmount_,
uint256 feeBasisPoints_,
address swapContract_,
address tokenPuller_,
bytes calldata swapCallData_
) external feeBasisPointsRespected(feeBasisPoints_) {
swapWithFeesOnInput(inAsset_, swapAmount_, feeBasisPoints_, swapContract_, tokenPuller_, swapCallData_);
}
function swapToEthWithFeesOnOutput(
address inAsset_,
uint256 swapAmount_,
uint256 feeBasisPoints_,
address swapContract_,
address tokenPuller_,
bytes calldata swapCallData_
) external noRenter feeBasisPointsRespected(feeBasisPoints_) {
uint256 startingETHBalance = address(this).balance;
SafeERC20.safeTransferFrom(IERC20(inAsset_), msg.sender, address(this), swapAmount_);
_performSwap(inAsset_, swapAmount_, swapContract_, tokenPuller_, swapCallData_);
_transferETH(
msg.sender,
getAmountWithoutFees(
address(this).balance - startingETHBalance,
feeBasisPoints_
)
);
}
function addToWhitelist(address[] calldata whitelist_) external onlyOwner {
_addToWhitelist(whitelist_);
}
function removeFromWhitelist(address[] calldata whitelist_) external onlyOwner {
_removeFromWhitelist(whitelist_);
}
function setFee(uint256 feeBasisPoints_) external onlyOwner {
_setFees(feeBasisPoints_);
}
function pullToken(address token_, address destination_) public onlyOwner {
if (destination_ == address(0)) revert ZeroAddress();
uint256 amount = IERC20(token_).balanceOf(address(this));
emit TokensPulled(token_, destination_, amount);
SafeERC20.safeTransfer(IERC20(token_), destination_, amount);
}
function pullTokens(address[] calldata tokens_, address destination_) external onlyOwner {
for (uint256 i; i < tokens_.length; ++i) {
pullToken(tokens_[i], destination_);
}
}
function pullETH(address destination_) external onlyOwner {
if (destination_ == address(0)) revert ZeroAddress();
uint256 amount = address(this).balance;
emit ETHPulled(destination_, amount);
_transferETH(destination_, amount);
}
function getAmountWithFees(uint256 amountWithoutFees_, uint256 feeBasisPoints_) public pure returns (uint256 amountWithFees_) {
amountWithFees_ = (amountWithoutFees_ * (10_000 + feeBasisPoints_)) / 10_000;
}
function getAmountWithoutFees(uint256 amountWithFees_, uint256 feeBasisPoints_) public pure returns (uint256 amountWithoutFees_) {
amountWithoutFees_ = (10_000 * amountWithFees_) / (10_000 + feeBasisPoints_);
}
function _addToWhitelist(address[] memory whitelist_) internal {
for (uint256 i; i < whitelist_.length; ++i) {
address account = whitelist_[i];
isWhitelisted[whitelist_[i]] = true;
emit ContractAddedToWhitelist(account);
}
}
function _performSwap(address inAsset_, uint256 swapAmount_, address swapContract_, address tokenPuller_, bytes calldata swapCallData_) internal {
if (!isWhitelisted[swapContract_]) revert ContractNotWhitelisted(swapContract_);
IERC20(inAsset_).approve(tokenPuller_, swapAmount_);
( bool success, bytes memory errorData ) = swapContract_.call(swapCallData_);
if (!success) revert SwapCallFailed(errorData);
IERC20(inAsset_).approve(tokenPuller_, 0);
}
function _performSwap(uint256 swapAmount_, address swapContract_, bytes calldata swapCallData_) internal {
if (!isWhitelisted[swapContract_]) revert ContractNotWhitelisted(swapContract_);
( bool success, bytes memory errorData ) = swapContract_.call{ value: swapAmount_ }(swapCallData_);
if (!success) revert SwapCallFailed(errorData);
}
function _removeFromWhitelist(address[] memory whitelist_) internal {
for (uint256 i; i < whitelist_.length; ++i) {
address account = whitelist_[i];
isWhitelisted[whitelist_[i]] = false;
emit ContractRemovedFromWhitelist(account);
}
}
function _setFees(uint256 feeBasisPoints_) internal {
emit FeeSet(feeBasisPoints = feeBasisPoints_);
}
function _transferETH(address destination_, uint256 amount_) internal {
( bool success, bytes memory errorData ) = destination_.call{ value: amount_ }("");
if (!success) revert ETHTransferFailed(errorData);
}
receive() external payable {}
}
文件 6 的 6:draft-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);
}
{
"compilationTarget": {
"contracts/SwapFeeRouter.sol": "SwapFeeRouter"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "none"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"owner_","type":"address"},{"internalType":"uint256","name":"feeBasisPoints_","type":"uint256"},{"internalType":"address[]","name":"whitelist_","type":"address[]"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"callee","type":"address"}],"name":"ContractNotWhitelisted","type":"error"},{"inputs":[{"internalType":"bytes","name":"errorData","type":"bytes"}],"name":"ETHTransferFailed","type":"error"},{"inputs":[{"internalType":"uint256","name":"expectedFeeBasisPoints_","type":"uint256"},{"internalType":"uint256","name":"actualFeeBasisPoints_","type":"uint256"}],"name":"FeeBasisPointsNotRespected","type":"error"},{"inputs":[],"name":"RenterAttempted","type":"error"},{"inputs":[{"internalType":"bytes","name":"errorData","type":"bytes"}],"name":"SwapCallFailed","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"contract_","type":"address"}],"name":"ContractAddedToWhitelist","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"contract_","type":"address"}],"name":"ContractRemovedFromWhitelist","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"destination_","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"ETHPulled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"feeBasisPoints_","type":"uint256"}],"name":"FeeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newOwner_","type":"address"}],"name":"OwnerSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner_","type":"address"}],"name":"PendingOwnerSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token_","type":"address"},{"indexed":true,"internalType":"address","name":"destination_","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"TokensPulled","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"whitelist_","type":"address[]"}],"name":"addToWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"feeBasisPoints","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountWithoutFees_","type":"uint256"},{"internalType":"uint256","name":"feeBasisPoints_","type":"uint256"}],"name":"getAmountWithFees","outputs":[{"internalType":"uint256","name":"amountWithFees_","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountWithFees_","type":"uint256"},{"internalType":"uint256","name":"feeBasisPoints_","type":"uint256"}],"name":"getAmountWithoutFees","outputs":[{"internalType":"uint256","name":"amountWithoutFees_","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isWhitelisted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","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":[{"internalType":"address","name":"destination_","type":"address"}],"name":"pullETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token_","type":"address"},{"internalType":"address","name":"destination_","type":"address"}],"name":"pullToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokens_","type":"address[]"},{"internalType":"address","name":"destination_","type":"address"}],"name":"pullTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"whitelist_","type":"address[]"}],"name":"removeFromWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"feeBasisPoints_","type":"uint256"}],"name":"setFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner_","type":"address"}],"name":"setPendingOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"feeBasisPoints_","type":"uint256"},{"internalType":"address","name":"swapContract_","type":"address"},{"internalType":"bytes","name":"swapCallData_","type":"bytes"}],"name":"swapFromEthWithFeesOnInput","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"outAsset_","type":"address"},{"internalType":"uint256","name":"feeBasisPoints_","type":"uint256"},{"internalType":"address","name":"swapContract_","type":"address"},{"internalType":"bytes","name":"swapCallData_","type":"bytes"}],"name":"swapFromEthWithFeesOnOutput","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"inAsset_","type":"address"},{"internalType":"uint256","name":"swapAmount_","type":"uint256"},{"internalType":"uint256","name":"feeBasisPoints_","type":"uint256"},{"internalType":"address","name":"swapContract_","type":"address"},{"internalType":"address","name":"tokenPuller_","type":"address"},{"internalType":"bytes","name":"swapCallData_","type":"bytes"}],"name":"swapToEthWithFeesOnInput","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"inAsset_","type":"address"},{"internalType":"uint256","name":"swapAmount_","type":"uint256"},{"internalType":"uint256","name":"feeBasisPoints_","type":"uint256"},{"internalType":"address","name":"swapContract_","type":"address"},{"internalType":"address","name":"tokenPuller_","type":"address"},{"internalType":"bytes","name":"swapCallData_","type":"bytes"}],"name":"swapToEthWithFeesOnOutput","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"inAsset_","type":"address"},{"internalType":"uint256","name":"swapAmount_","type":"uint256"},{"internalType":"uint256","name":"feeBasisPoints_","type":"uint256"},{"internalType":"address","name":"swapContract_","type":"address"},{"internalType":"address","name":"tokenPuller_","type":"address"},{"internalType":"bytes","name":"swapCallData_","type":"bytes"}],"name":"swapWithFeesOnInput","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"inAsset_","type":"address"},{"internalType":"uint256","name":"swapAmount_","type":"uint256"},{"internalType":"address","name":"outAsset_","type":"address"},{"internalType":"uint256","name":"feeBasisPoints_","type":"uint256"},{"internalType":"address","name":"swapContract_","type":"address"},{"internalType":"address","name":"tokenPuller_","type":"address"},{"internalType":"bytes","name":"swapCallData_","type":"bytes"}],"name":"swapWithFeesOnOutput","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]