文件 1 的 1:ParaswapWrapper.sol
pragma solidity =0.8.10;
contract MainnetAuthAddresses {
address internal constant ADMIN_VAULT_ADDR = 0xCCf3d848e08b94478Ed8f46fFead3008faF581fD;
address internal constant DSGUARD_FACTORY_ADDRESS = 0x5a15566417e6C1c9546523066500bDDBc53F88C7;
address internal constant ADMIN_ADDR = 0x25eFA336886C74eA8E282ac466BdCd0199f85BB9;
address internal constant PROXY_AUTH_ADDRESS = 0x149667b6FAe2c63D1B4317C716b0D0e4d3E2bD70;
address internal constant MODULE_AUTH_ADDRESS = 0x7407974DDBF539e552F1d051e44573090912CC3D;
}
contract AuthHelper is MainnetAuthAddresses {
}
contract AdminVault is AuthHelper {
address public owner;
address public admin;
error SenderNotAdmin();
constructor() {
owner = msg.sender;
admin = ADMIN_ADDR;
}
function changeOwner(address _owner) public {
if (admin != msg.sender){
revert SenderNotAdmin();
}
owner = _owner;
}
function changeAdmin(address _admin) public {
if (admin != msg.sender){
revert SenderNotAdmin();
}
admin = _admin;
}
}
interface IERC20 {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint256 digits);
function totalSupply() external view returns (uint256 supply);
function balanceOf(address _owner) external view returns (uint256 balance);
function transfer(address _to, uint256 _value) external returns (bool success);
function transferFrom(
address _from,
address _to,
uint256 _value
) external returns (bool success);
function approve(address _spender, uint256 _value) external returns (bool success);
function allowance(address _owner, address _spender) external view returns (uint256 remaining);
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
}
library Address {
error InsufficientBalance(uint256 available, uint256 required);
error SendingValueFail();
error InsufficientBalanceForCall(uint256 available, uint256 required);
error NonContractCall();
function isContract(address account) internal view returns (bool) {
bytes32 codehash;
bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
assembly {
codehash := extcodehash(account)
}
return (codehash != accountHash && codehash != 0x0);
}
function sendValue(address payable recipient, uint256 amount) internal {
uint256 balance = address(this).balance;
if (balance < amount){
revert InsufficientBalance(balance, amount);
}
(bool success, ) = recipient.call{value: amount}("");
if (!(success)){
revert SendingValueFail();
}
}
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) {
uint256 balance = address(this).balance;
if (balance < value){
revert InsufficientBalanceForCall(balance, value);
}
return _functionCallWithValue(target, data, value, errorMessage);
}
function _functionCallWithValue(
address target,
bytes memory data,
uint256 weiValue,
string memory errorMessage
) private returns (bytes memory) {
if (!(isContract(target))){
revert NonContractCall();
}
(bool success, bytes memory returndata) = target.call{value: weiValue}(data);
if (success) {
return returndata;
} else {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
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 {
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 _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(token).code.length > 0;
}
}
contract AdminAuth is AuthHelper {
using SafeERC20 for IERC20;
AdminVault public constant adminVault = AdminVault(ADMIN_VAULT_ADDR);
error SenderNotOwner();
error SenderNotAdmin();
modifier onlyOwner() {
if (adminVault.owner() != msg.sender){
revert SenderNotOwner();
}
_;
}
modifier onlyAdmin() {
if (adminVault.admin() != msg.sender){
revert SenderNotAdmin();
}
_;
}
function withdrawStuckFunds(address _token, address _receiver, uint256 _amount) public onlyOwner {
if (_token == 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) {
payable(_receiver).transfer(_amount);
} else {
IERC20(_token).safeTransfer(_receiver, _amount);
}
}
function kill() public onlyAdmin {
selfdestruct(payable(msg.sender));
}
}
contract Discount is AdminAuth{
mapping(address => bool) public serviceFeesDisabled;
function reenableServiceFee(address _wallet) public onlyOwner{
serviceFeesDisabled[_wallet] = false;
}
function disableServiceFee(address _wallet) public onlyOwner{
serviceFeesDisabled[_wallet] = true;
}
}
abstract contract IWETH {
function allowance(address, address) public virtual view returns (uint256);
function balanceOf(address) public virtual view returns (uint256);
function approve(address, uint256) public virtual;
function transfer(address, uint256) public virtual returns (bool);
function transferFrom(
address,
address,
uint256
) public virtual returns (bool);
function deposit() public payable virtual;
function withdraw(uint256) public virtual;
}
library TokenUtils {
using SafeERC20 for IERC20;
address public constant WETH_ADDR = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address public constant ETH_ADDR = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
function approveToken(
address _tokenAddr,
address _to,
uint256 _amount
) internal {
if (_tokenAddr == ETH_ADDR) return;
if (IERC20(_tokenAddr).allowance(address(this), _to) < _amount) {
IERC20(_tokenAddr).safeApprove(_to, _amount);
}
}
function pullTokensIfNeeded(
address _token,
address _from,
uint256 _amount
) internal returns (uint256) {
if (_amount == type(uint256).max) {
_amount = getBalance(_token, _from);
}
if (_from != address(0) && _from != address(this) && _token != ETH_ADDR && _amount != 0) {
IERC20(_token).safeTransferFrom(_from, address(this), _amount);
}
return _amount;
}
function withdrawTokens(
address _token,
address _to,
uint256 _amount
) internal returns (uint256) {
if (_amount == type(uint256).max) {
_amount = getBalance(_token, address(this));
}
if (_to != address(0) && _to != address(this) && _amount != 0) {
if (_token != ETH_ADDR) {
IERC20(_token).safeTransfer(_to, _amount);
} else {
(bool success, ) = _to.call{value: _amount}("");
require(success, "Eth send fail");
}
}
return _amount;
}
function depositWeth(uint256 _amount) internal {
IWETH(WETH_ADDR).deposit{value: _amount}();
}
function withdrawWeth(uint256 _amount) internal {
IWETH(WETH_ADDR).withdraw(_amount);
}
function getBalance(address _tokenAddr, address _acc) internal view returns (uint256) {
if (_tokenAddr == ETH_ADDR) {
return _acc.balance;
} else {
return IERC20(_tokenAddr).balanceOf(_acc);
}
}
function getTokenDecimals(address _token) internal view returns (uint256) {
if (_token == ETH_ADDR) return 18;
return IERC20(_token).decimals();
}
}
contract DFSExchangeHelper {
using TokenUtils for address;
error InvalidOffchainData();
error OutOfRangeSlicingError();
error ZeroTokensSwapped();
using SafeERC20 for IERC20;
function sendLeftover(
address _srcAddr,
address _destAddr,
address payable _to
) internal {
TokenUtils.ETH_ADDR.withdrawTokens(_to, type(uint256).max);
_srcAddr.withdrawTokens(_to, type(uint256).max);
_destAddr.withdrawTokens(_to, type(uint256).max);
}
function sliceUint(bytes memory bs, uint256 start) internal pure returns (uint256) {
if (bs.length < start + 32){
revert OutOfRangeSlicingError();
}
uint256 x;
assembly {
x := mload(add(bs, add(0x20, start)))
}
return x;
}
function writeUint256(
bytes memory _b,
uint256 _index,
uint256 _input
) internal pure {
if (_b.length < _index + 32) {
revert InvalidOffchainData();
}
bytes32 input = bytes32(_input);
_index += 32;
assembly {
mstore(add(_b, _index), input)
}
}
}
contract DFSExchangeData {
struct OffchainData {
address wrapper;
address exchangeAddr;
address allowanceTarget;
uint256 price;
uint256 protocolFee;
bytes callData;
}
struct ExchangeData {
address srcAddr;
address destAddr;
uint256 srcAmount;
uint256 destAmount;
uint256 minPrice;
uint256 dfsFeeDivider;
address user;
address wrapper;
bytes wrapperData;
OffchainData offchainData;
}
}
abstract contract IOffchainWrapper is DFSExchangeData {
function takeOrder(
ExchangeData memory _exData
) virtual public payable returns (bool success, uint256);
}
contract ParaswapWrapper is IOffchainWrapper, DFSExchangeHelper, AdminAuth {
using TokenUtils for address;
using SafeERC20 for IERC20;
struct ParaswapCalldata{
bytes realCalldata;
uint256 offset;
}
function takeOrder(
ExchangeData memory _exData
) override public payable returns (bool success, uint256) {
ParaswapCalldata memory paraswapCalldata = abi.decode(_exData.offchainData.callData, (ParaswapCalldata));
IERC20(_exData.srcAddr).safeApprove(_exData.offchainData.allowanceTarget, _exData.srcAmount);
writeUint256(paraswapCalldata.realCalldata, paraswapCalldata.offset, _exData.srcAmount);
uint256 tokensBefore = _exData.destAddr.getBalance(address(this));
(success, ) = _exData.offchainData.exchangeAddr.call(paraswapCalldata.realCalldata);
uint256 tokensSwapped = 0;
if (success) {
tokensSwapped = _exData.destAddr.getBalance(address(this)) - tokensBefore;
if (tokensSwapped == 0){
revert ZeroTokensSwapped();
}
}
sendLeftover(_exData.srcAddr, _exData.destAddr, payable(msg.sender));
return (success, tokensSwapped);
}
receive() external virtual payable {}
}