编译器
0.8.24+commit.e11b9ed9
文件 1 的 7:Address.sol
pragma solidity ^0.8.20;
library Address {
error AddressInsufficientBalance(address account);
error AddressEmptyCode(address target);
error FailedInnerCall();
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
function _revert(bytes memory returndata) private pure {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}
文件 2 的 7:DeriskRouter.sol
pragma solidity ^0.8.24;
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "./interfaces/IDEXRouter.sol";
import "./interfaces/IDEXFactory.sol";
import "./interfaces/IDEXPair.sol";
import "./interfaces/IERC20.sol";
interface IStaking {
function stakedBalanceOf(address account) external view returns (uint256);
}
contract DeriskRouter {
using ECDSA for bytes32;
using Address for address payable;
IDEXRouter private constant uniswapV2Router = IDEXRouter(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
address private constant factory = 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f;
address private constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
IERC20 private constant APES = IERC20(0x09675e24CA1EB06023451AC8088EcA1040F47585);
uint256 private constant FEE_CAP = 500;
address payable public owner;
address payable public feeCollector;
mapping(address => bool) public isExecutor;
mapping(address => mapping(address => uint256)) public lastExecutedNonce;
address public stakingContract;
uint256[3] public tiers = [1250000 * 10**18, 250000 * 10**18, 100000 * 10**18];
uint256[4] public feesPerTier = [0, 25, 50, 100];
bytes32 public DOMAIN_SEPARATOR = keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256("Apescreener"),
keccak256("1"),
block.chainid,
address(this)
)
);
bytes32 public constant PERMIT_TYPEHASH = keccak256("Data(address wallet,address token,uint256 nonce,uint256 deadline)");
event Derisked(address wallet, address token, uint256 nonce);
error INVALID_SIGNATURE();
error ORDER_EXPIRED();
error ORDER_ALREADY_EXECUTED();
error NOT_AUTHORIZED();
error INVALID_TX();
error INVALID_ADDRESS();
constructor(address payable _owner) {
owner = payable(_owner);
feeCollector = _owner;
isExecutor[msg.sender] = true;
}
function derisk(address payable userWallet, address token, uint256 nonce, uint256 signatureDeadline, uint256 amountIn, uint256 minAmountOut, bytes calldata signature) external {
uint256 gasFees;
uint256 gasLimit = gasleft() + 25000;
if (msg.sender != userWallet) {
gasFees = gasLimit * tx.gasprice;
if (lastExecutedNonce[userWallet][token] >= nonce) revert ORDER_ALREADY_EXECUTED();
if (!isExecutor[msg.sender]) revert NOT_AUTHORIZED();
if (signatureDeadline < block.timestamp) revert ORDER_EXPIRED();
bytes32 digest = keccak256(abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR, keccak256(abi.encode(PERMIT_TYPEHASH, userWallet, token, nonce, signatureDeadline))));
address recoveredAddress = digest.recover(signature);
if (recoveredAddress != userWallet) revert INVALID_SIGNATURE();
}
address[] memory path = new address[](2);
path[0] = token;
path[1] = WETH;
_swapExactTokensForETHSupportingFeeOnTransferTokens(amountIn, minAmountOut, path, userWallet);
uint256 fees = address(this).balance * getFeesBps(userWallet) / 10000;
feeCollector.sendValue(fees + gasFees);
userWallet.sendValue(address(this).balance);
lastExecutedNonce[userWallet][token] = nonce;
emit Derisked(userWallet, token, nonce);
}
function getFeesBps(address userWallet) public view returns (uint256) {
uint256 balanceUser = APES.balanceOf(userWallet);
if (stakingContract != address(0)) {
balanceUser += IStaking(stakingContract).stakedBalanceOf(userWallet);
}
if (balanceUser > tiers[0]) {
return feesPerTier[0];
} else if (balanceUser > tiers[1]) {
return feesPerTier[1];
} else if (balanceUser > tiers[2]) {
return feesPerTier[2];
} else {
return feesPerTier[3];
}
}
function _swapExactTokensForETHSupportingFeeOnTransferTokens(
uint amountIn,
uint amountOutMin,
address[] memory path,
address userWallet
)
internal
{
address pair = IDEXFactory(factory).getPair(path[0], path[1]);
_safeTransferFrom(path[0], userWallet, pair, amountIn);
_swapSupportingFeeOnTransferTokens(path, IDEXPair(pair));
uint amountOut = IERC20(WETH).balanceOf(address(this));
require(amountOut >= amountOutMin, 'INSUFFICIENT_OUTPUT_AMOUNT');
IERC20(WETH).withdraw(amountOut);
}
function _swapSupportingFeeOnTransferTokens(address[] memory path, IDEXPair pair) internal virtual {
for (uint i; i < path.length - 1; i++) {
(address input, address output) = (path[i], path[i + 1]);
(address token0,) = _sortTokens(input, output);
uint amountInput;
uint amountOutput;
{
(uint reserve0, uint reserve1,) = pair.getReserves();
(uint reserveInput, uint reserveOutput) = input == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
amountInput = IERC20(input).balanceOf(address(pair)) - reserveInput;
amountOutput = uniswapV2Router.getAmountOut(amountInput, reserveInput, reserveOutput);
}
(uint amount0Out, uint amount1Out) = input == token0 ? (uint(0), amountOutput) : (amountOutput, uint(0));
pair.swap(amount0Out, amount1Out, address(this), new bytes(0));
}
}
function _sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) {
require(tokenA != tokenB, 'IDENTICAL_ADDRESSES');
(token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
require(token0 != address(0), 'ZERO_ADDRESS');
}
function _safeTransferFrom(
address token,
address from,
address to,
uint256 value
) internal {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
require(
success && (data.length == 0 || abi.decode(data, (bool))),
'transferFrom failed'
);
}
function setStakingContract(address _stakingContract) external {
if (msg.sender != owner) revert NOT_AUTHORIZED();
stakingContract = _stakingContract;
}
function setFeeCollector(address payable _feeCollector) external {
if (_feeCollector == address(0)) revert INVALID_ADDRESS();
if (msg.sender != owner) revert NOT_AUTHORIZED();
feeCollector = _feeCollector;
}
function setExecutor(address executor, bool value) external {
if (msg.sender != owner) revert NOT_AUTHORIZED();
isExecutor[executor] = value;
}
function setOwner(address payable newOwner) external {
if (newOwner == address(0)) revert INVALID_ADDRESS();
if (msg.sender != owner) revert NOT_AUTHORIZED();
owner = newOwner;
}
function setTiers(uint256 _tiers1, uint256 _tiers2, uint256 _tiers3) external {
if (msg.sender != owner) revert NOT_AUTHORIZED();
tiers = [_tiers1 * 10**18, _tiers2 * 10**18, _tiers3 * 10**18];
}
function setFees(uint256 _fee1, uint256 _fee2, uint256 _fee3, uint256 _fee4) external {
if (msg.sender != owner) revert NOT_AUTHORIZED();
require(_fee1 <= FEE_CAP && _fee2 <= FEE_CAP && _fee3 <= FEE_CAP && _fee4 <= FEE_CAP, "Fees must be less than 500 bps");
feesPerTier = [_fee1, _fee2, _fee3, _fee4];
}
receive() external payable {}
}
文件 3 的 7:ECDSA.sol
pragma solidity ^0.8.20;
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS
}
error ECDSAInvalidSignature();
error ECDSAInvalidSignatureLength(uint256 length);
error ECDSAInvalidSignatureS(bytes32 s);
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
}
}
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
_throwError(error, errorArg);
return recovered;
}
function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) {
unchecked {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
uint8 v = uint8((uint256(vs) >> 255) + 27);
return tryRecover(hash, v, r, s);
}
}
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
_throwError(error, errorArg);
return recovered;
}
function tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address, RecoverError, bytes32) {
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS, s);
}
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature, bytes32(0));
}
return (signer, RecoverError.NoError, bytes32(0));
}
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);
_throwError(error, errorArg);
return recovered;
}
function _throwError(RecoverError error, bytes32 errorArg) private pure {
if (error == RecoverError.NoError) {
return;
} else if (error == RecoverError.InvalidSignature) {
revert ECDSAInvalidSignature();
} else if (error == RecoverError.InvalidSignatureLength) {
revert ECDSAInvalidSignatureLength(uint256(errorArg));
} else if (error == RecoverError.InvalidSignatureS) {
revert ECDSAInvalidSignatureS(errorArg);
}
}
}
文件 4 的 7:IDEXFactory.sol
pragma solidity ^0.8.13;
interface IDEXFactory {
function createPair(address tokenA, address tokenB) external returns (address pair);
function getPair(address tokenA, address tokenB) external view returns (address pair);
}
文件 5 的 7:IDEXPair.sol
pragma solidity ^0.8.13;
interface IDEXPair {
function token0() external view returns (address);
function token1() external view returns (address);
function sync() external;
function price0CumulativeLast() external view returns (uint256);
function price1CumulativeLast() external view returns (uint256);
function getReserves()
external
view
returns (
uint112 reserve0,
uint112 reserve1,
uint32 blockTimestampLast
);
function swap(
uint256 amount0Out,
uint256 amount1Out,
address to,
bytes calldata data
) external;
}
文件 6 的 7:IDEXRouter.sol
pragma solidity ^0.8.24;
interface IDEXRouter {
function factory() external pure returns (address);
function WETH() external pure returns (address);
function addLiquidity(
address tokenA,
address tokenB,
uint256 amountADesired,
uint256 amountBDesired,
uint256 amountAMin,
uint256 amountBMin,
address to,
uint256 deadline
)
external
returns (
uint256 amountA,
uint256 amountB,
uint256 liquidity
);
function addLiquidityETH(
address token,
uint256 amountTokenDesired,
uint256 amountTokenMin,
uint256 amountETHMin,
address to,
uint256 deadline
)
external
payable
returns (
uint256 amountToken,
uint256 amountETH,
uint256 liquidity
);
function swapExactTokensForTokensSupportingFeeOnTransferTokens(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external;
function swapExactETHForTokensSupportingFeeOnTransferTokens(
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external payable;
function swapExactTokensForETHSupportingFeeOnTransferTokens(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external;
function removeLiquidity(
address tokenA,
address tokenB,
uint256 liquidity,
uint256 amountAMin,
uint256 amountBMin,
address to,
uint256 deadline
) external returns (uint256 amountA, uint256 amountB);
function swapExactTokensForTokens(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external returns (uint256[] memory amounts);
function swapTokensForExactTokens(
uint256 amountOut,
uint256 amountInMax,
address[] calldata path,
address to,
uint256 deadline
) external returns (uint256[] memory amounts);
function getAmountsOut(uint256 amountIn, address[] calldata path) external view returns (uint256[] memory amounts);
function getAmountOut(uint256 amountIn, uint256 reserveIn, uint256 reserveOut) external pure returns (uint256 amountOut);
}
文件 7 的 7:IERC20.sol
pragma solidity ^0.8.13;
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, 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 sender, address recipient, uint256 amount) external returns (bool);
function decimals() external view returns (uint8);
function withdraw(uint) external;
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
{
"compilationTarget": {
"contracts/DeriskRouter.sol": "DeriskRouter"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address payable","name":"_owner","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"ECDSAInvalidSignature","type":"error"},{"inputs":[{"internalType":"uint256","name":"length","type":"uint256"}],"name":"ECDSAInvalidSignatureLength","type":"error"},{"inputs":[{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"ECDSAInvalidSignatureS","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"INVALID_ADDRESS","type":"error"},{"inputs":[],"name":"INVALID_SIGNATURE","type":"error"},{"inputs":[],"name":"INVALID_TX","type":"error"},{"inputs":[],"name":"NOT_AUTHORIZED","type":"error"},{"inputs":[],"name":"ORDER_ALREADY_EXECUTED","type":"error"},{"inputs":[],"name":"ORDER_EXPIRED","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"wallet","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"Derisked","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMIT_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address payable","name":"userWallet","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"signatureDeadline","type":"uint256"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"minAmountOut","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"derisk","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"feeCollector","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"feesPerTier","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"userWallet","type":"address"}],"name":"getFeesBps","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isExecutor","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"lastExecutedNonce","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"executor","type":"address"},{"internalType":"bool","name":"value","type":"bool"}],"name":"setExecutor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"_feeCollector","type":"address"}],"name":"setFeeCollector","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_fee1","type":"uint256"},{"internalType":"uint256","name":"_fee2","type":"uint256"},{"internalType":"uint256","name":"_fee3","type":"uint256"},{"internalType":"uint256","name":"_fee4","type":"uint256"}],"name":"setFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"newOwner","type":"address"}],"name":"setOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_stakingContract","type":"address"}],"name":"setStakingContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tiers1","type":"uint256"},{"internalType":"uint256","name":"_tiers2","type":"uint256"},{"internalType":"uint256","name":"_tiers3","type":"uint256"}],"name":"setTiers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stakingContract","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"tiers","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]