编译器
0.8.18+commit.87f61d96
文件 1 的 20:ArbswapSmartRouter.sol
pragma solidity ^0.8.4;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "./interfaces/IStableSwap.sol";
import "./interfaces/IStableSwapFactory.sol";
import "./interfaces/IArbswapV2Factory.sol";
import "./interfaces/IWETH02.sol";
import "./libraries/UniERC20.sol";
import "./libraries/SafeERC20.sol";
import "./libraries/ArbswapV2ExchangeLib.sol";
contract ArbswapSmartRouter is Ownable, ReentrancyGuard {
using UniERC20 for IERC20;
using SafeERC20 for IERC20;
using ArbswapV2ExchangeLib for IArbswapV2Exchange;
enum FLAG {
STABLE_SWAP,
V2_EXACT_IN
}
IWETH02 public immutable weth;
address public immutable arbswapV2;
address public stableswapFactory;
event NewStableSwapFactory(address indexed sender, address indexed factory);
event SwapMulti(
address indexed sender,
address indexed srcTokenAddr,
address indexed dstTokenAddr,
uint256 srcAmount
);
event Swap(
address indexed sender,
address indexed srcTokenAddr,
address indexed dstTokenAddr,
uint256 srcAmount
);
fallback() external {}
receive() external payable {}
constructor(
address _WETHAddress,
address _arbswapV2,
address _stableswapFactory
) {
weth = IWETH02(_WETHAddress);
arbswapV2 = _arbswapV2;
stableswapFactory = _stableswapFactory;
}
function setStableSwapFactory(address _factory) external onlyOwner {
require(
_factory != address(0),
"StableSwap factory cannot be zero address"
);
stableswapFactory = _factory;
emit NewStableSwapFactory(msg.sender, stableswapFactory);
}
function swapMulti(
IERC20[] calldata tokens,
uint256 amount,
uint256 minReturn,
FLAG[] calldata flags
) public payable nonReentrant returns (uint256 returnAmount) {
require(tokens.length == flags.length + 1, "swapMulti: wrong length");
IERC20 srcToken = tokens[0];
IERC20 dstToken = tokens[tokens.length - 1];
if (srcToken == dstToken) {
return amount;
}
srcToken.uniTransferFrom(payable(msg.sender), address(this), amount);
uint256 receivedAmount = srcToken.uniBalanceOf(address(this));
for (uint256 i = 1; i < tokens.length; i++) {
if (tokens[i - 1] == tokens[i]) {
continue;
}
if (flags[i - 1] == FLAG.STABLE_SWAP) {
_swapOnStableSwap(
tokens[i - 1],
tokens[i],
tokens[i - 1].uniBalanceOf(address(this))
);
} else if (flags[i - 1] == FLAG.V2_EXACT_IN) {
_swapOnV2ExactIn(
tokens[i - 1],
tokens[i],
tokens[i - 1].uniBalanceOf(address(this))
);
}
}
returnAmount = dstToken.uniBalanceOf(address(this));
require(
returnAmount >= minReturn,
"swapMulti: return amount is less than minReturn"
);
uint256 inRefund = srcToken.uniBalanceOf(address(this));
emit SwapMulti(
msg.sender,
address(srcToken),
address(dstToken),
receivedAmount - inRefund
);
uint256 userBalanceBefore = dstToken.uniBalanceOf(msg.sender);
dstToken.uniTransfer(payable(msg.sender), returnAmount);
require(
dstToken.uniBalanceOf(msg.sender) - userBalanceBefore >= minReturn,
"swapMulti: incorrect user balance"
);
srcToken.uniTransfer(payable(msg.sender), inRefund);
}
function swap(
IERC20 srcToken,
IERC20 dstToken,
uint256 amount,
uint256 minReturn,
FLAG flag
) public payable nonReentrant returns (uint256 returnAmount) {
if (srcToken == dstToken) {
return amount;
}
srcToken.uniTransferFrom(payable(msg.sender), address(this), amount);
uint256 receivedAmount = srcToken.uniBalanceOf(address(this));
if (flag == FLAG.STABLE_SWAP) {
_swapOnStableSwap(srcToken, dstToken, receivedAmount);
} else if (flag == FLAG.V2_EXACT_IN) {
_swapOnV2ExactIn(srcToken, dstToken, receivedAmount);
}
returnAmount = dstToken.uniBalanceOf(address(this));
require(
returnAmount >= minReturn,
"swap: return amount is less than minReturn"
);
uint256 inRefund = srcToken.uniBalanceOf(address(this));
emit Swap(
msg.sender,
address(srcToken),
address(dstToken),
receivedAmount - inRefund
);
uint256 userBalanceBefore = dstToken.uniBalanceOf(msg.sender);
dstToken.uniTransfer(payable(msg.sender), returnAmount);
require(
dstToken.uniBalanceOf(msg.sender) - userBalanceBefore >= minReturn,
"swap: incorrect user balance"
);
srcToken.uniTransfer(payable(msg.sender), inRefund);
}
function _swapOnStableSwap(
IERC20 srcToken,
IERC20 dstToken,
uint256 amount
) internal {
require(
stableswapFactory != address(0),
"StableSwap factory cannot be zero address"
);
if (srcToken.isETH()) {
weth.deposit{value: amount}();
}
IERC20 srcTokenReal = srcToken.isETH() ? weth : srcToken;
IERC20 dstTokenReal = dstToken.isETH() ? weth : dstToken;
IStableSwapFactory.StableSwapPairInfo memory info = IStableSwapFactory(
stableswapFactory
).getPairInfo(address(srcTokenReal), address(dstTokenReal));
if (info.swapContract == address(0)) {
return;
}
IStableSwap stableSwap = IStableSwap(info.swapContract);
IERC20[] memory tokens = new IERC20[](2);
tokens[0] = IERC20(stableSwap.coins(uint256(0)));
tokens[1] = IERC20(stableSwap.coins(uint256(1)));
uint256 i = (srcTokenReal == tokens[0] ? 1 : 0) +
(srcTokenReal == tokens[1] ? 2 : 0);
uint256 j = (dstTokenReal == tokens[0] ? 1 : 0) +
(dstTokenReal == tokens[1] ? 2 : 0);
srcTokenReal.uniApprove(address(stableSwap), amount);
stableSwap.exchange(i - 1, j - 1, amount, 0);
if (dstToken.isETH()) {
weth.withdraw(weth.balanceOf(address(this)));
}
}
function _swapOnV2ExactIn(
IERC20 srcToken,
IERC20 dstToken,
uint256 amount
) internal returns (uint256 returnAmount) {
if (srcToken.isETH()) {
weth.deposit{value: amount}();
}
IERC20 srcTokenReal = srcToken.isETH() ? weth : srcToken;
IERC20 dstTokenReal = dstToken.isETH() ? weth : dstToken;
IArbswapV2Exchange exchange = IArbswapV2Factory(arbswapV2).getPair(
srcTokenReal,
dstTokenReal
);
srcTokenReal.safeTransfer(address(exchange), amount);
bool needSync;
(returnAmount, needSync) = exchange.getReturn(
srcTokenReal,
dstTokenReal,
amount
);
if (needSync) {
exchange.sync();
}
if (srcTokenReal < dstTokenReal) {
exchange.swap(0, returnAmount, address(this), "");
} else {
exchange.swap(returnAmount, 0, address(this), "");
}
if (dstToken.isETH()) {
weth.withdraw(weth.balanceOf(address(this)));
}
}
}
文件 2 的 20:ArbswapV2ExchangeLib.sol
pragma solidity ^0.8.4;
import "../interfaces/IArbswapV2Factory.sol";
import "../interfaces/IArbswapV2Exchange.sol";
import "./UniERC20.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";
library ArbswapV2ExchangeLib {
using Math for uint256;
using UniERC20 for IERC20;
function getReturn(
IArbswapV2Exchange exchange,
IERC20 srcToken,
IERC20 dstToken,
uint256 amountIn
) internal view returns (uint256 result, bool needSync) {
uint256 reserveIn = srcToken.uniBalanceOf(address(exchange));
uint256 reserveOut = dstToken.uniBalanceOf(address(exchange));
(uint112 reserve0, uint112 reserve1, ) = exchange.getReserves();
if (srcToken > dstToken) {
(reserve0, reserve1) = (reserve1, reserve0);
}
amountIn = reserveIn - reserve0;
needSync = (reserveIn < reserve0 || reserveOut < reserve1);
uint256 amountInWithFee = amountIn * 997;
uint256 numerator = amountInWithFee * Math.min(reserveOut, reserve1);
uint256 denominator = Math.min(reserveIn, reserve0) *
1000 +
amountInWithFee;
result = (denominator == 0) ? 0 : numerator / denominator;
}
}
文件 3 的 20: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 的 20:IArbswapV2Exchange.sol
pragma solidity ^0.8.0;
interface IArbswapV2Exchange {
function getReserves()
external
view
returns (
uint112 _reserve0,
uint112 _reserve1,
uint32 _blockTimestampLast
);
function swap(
uint256 amount0Out,
uint256 amount1Out,
address to,
bytes calldata data
) external;
function skim(address to) external;
function sync() external;
}
文件 5 的 20:IArbswapV2Factory.sol
pragma solidity ^0.8.0;
import "./IArbswapV2Exchange.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IArbswapV2Factory {
function getPair(
IERC20 tokenA,
IERC20 tokenB
) external view returns (IArbswapV2Exchange pair);
}
文件 6 的 20:IDaiLikePermit.sol
pragma solidity ^0.8.0;
pragma abicoder v1;
interface IDaiLikePermit {
function permit(
address holder,
address spender,
uint256 nonce,
uint256 expiry,
bool allowed,
uint8 v,
bytes32 r,
bytes32 s
) external;
}
文件 7 的 20: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);
}
文件 8 的 20:IERC20Metadata.sol
pragma solidity ^0.8.0;
import "../IERC20.sol";
interface IERC20Metadata is IERC20 {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
}
文件 9 的 20:IERC20MetadataUppercase.sol
pragma solidity ^0.8.0;
pragma abicoder v1;
interface IERC20MetadataUppercase {
function NAME() external view returns (string memory);
function SYMBOL() external view returns (string memory);
}
文件 10 的 20:IStableSwap.sol
pragma solidity ^0.8.0;
interface IStableSwap {
function get_dy(
uint256 i,
uint256 j,
uint256 dx
) external view returns (uint256 dy);
function exchange(
uint256 i,
uint256 j,
uint256 dx,
uint256 minDy
) external payable;
function coins(uint256 i) external view returns (address);
function balances(uint256 i) external view returns (uint256);
function A() external view returns (uint256);
function fee() external view returns (uint256);
}
文件 11 的 20:IStableSwapFactory.sol
pragma solidity ^0.8.0;
interface IStableSwapFactory {
struct StableSwapPairInfo {
address swapContract;
address token0;
address token1;
address LPContract;
}
function pairLength() external view returns (uint256);
function getPairInfo(
address _tokenA,
address _tokenB
) external view returns (StableSwapPairInfo memory info);
}
文件 12 的 20:IWETH02.sol
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IWETH02 is IERC20 {
function deposit() external payable;
function withdraw(uint256 amount) external;
}
文件 13 的 20:Math.sol
pragma solidity ^0.8.0;
library Math {
enum Rounding {
Down,
Up,
Zero
}
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
function average(uint256 a, uint256 b) internal pure returns (uint256) {
return (a & b) + (a ^ b) / 2;
}
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
return a == 0 ? 0 : (a - 1) / b + 1;
}
function mulDiv(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 result) {
unchecked {
uint256 prod0;
uint256 prod1;
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
if (prod1 == 0) {
return prod0 / denominator;
}
require(denominator > prod1);
uint256 remainder;
assembly {
remainder := mulmod(x, y, denominator)
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
uint256 twos = denominator & (~denominator + 1);
assembly {
denominator := div(denominator, twos)
prod0 := div(prod0, twos)
twos := add(div(sub(0, twos), twos), 1)
}
prod0 |= prod1 * twos;
uint256 inverse = (3 * denominator) ^ 2;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
result = prod0 * inverse;
return result;
}
}
function mulDiv(
uint256 x,
uint256 y,
uint256 denominator,
Rounding rounding
) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 result = 1 << (log2(a) >> 1);
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10**64) {
value /= 10**64;
result += 64;
}
if (value >= 10**32) {
value /= 10**32;
result += 32;
}
if (value >= 10**16) {
value /= 10**16;
result += 16;
}
if (value >= 10**8) {
value /= 10**8;
result += 8;
}
if (value >= 10**4) {
value /= 10**4;
result += 4;
}
if (value >= 10**2) {
value /= 10**2;
result += 2;
}
if (value >= 10**1) {
result += 1;
}
}
return result;
}
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
}
}
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
}
}
}
文件 14 的 20: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);
}
}
文件 15 的 20: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;
}
}
文件 16 的 20:RevertReasonForwarder.sol
pragma solidity ^0.8.0;
pragma abicoder v1;
library RevertReasonForwarder {
function reRevert() internal pure {
assembly {
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
}
}
文件 17 的 20:SafeERC20.sol
pragma solidity ^0.8.0;
pragma abicoder v1;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol";
import "../interfaces/IDaiLikePermit.sol";
import "./RevertReasonForwarder.sol";
library SafeERC20 {
error SafeTransferFailed();
error SafeTransferFromFailed();
error ForceApproveFailed();
error SafeIncreaseAllowanceFailed();
error SafeDecreaseAllowanceFailed();
error SafePermitBadLength();
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 amount
) internal {
bytes4 selector = token.transferFrom.selector;
bool success;
assembly {
let data := mload(0x40)
mstore(data, selector)
mstore(add(data, 0x04), from)
mstore(add(data, 0x24), to)
mstore(add(data, 0x44), amount)
success := call(gas(), token, 0, data, 100, 0x0, 0x20)
if success {
switch returndatasize()
case 0 {
success := gt(extcodesize(token), 0)
}
default {
success := and(gt(returndatasize(), 31), eq(mload(0), 1))
}
}
}
if (!success) revert SafeTransferFromFailed();
}
function safeTransfer(IERC20 token, address to, uint256 value) internal {
if (!_makeCall(token, token.transfer.selector, to, value)) {
revert SafeTransferFailed();
}
}
function forceApprove(
IERC20 token,
address spender,
uint256 value
) internal {
if (!_makeCall(token, token.approve.selector, spender, value)) {
if (
!_makeCall(token, token.approve.selector, spender, 0) ||
!_makeCall(token, token.approve.selector, spender, value)
) {
revert ForceApproveFailed();
}
}
}
function safeIncreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 allowance = token.allowance(address(this), spender);
if (value > type(uint256).max - allowance)
revert SafeIncreaseAllowanceFailed();
forceApprove(token, spender, allowance + value);
}
function safeDecreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 allowance = token.allowance(address(this), spender);
if (value > allowance) revert SafeDecreaseAllowanceFailed();
forceApprove(token, spender, allowance - value);
}
function safePermit(IERC20 token, bytes calldata permit) internal {
if (!tryPermit(token, permit)) RevertReasonForwarder.reRevert();
}
function tryPermit(
IERC20 token,
bytes calldata permit
) internal returns (bool) {
if (permit.length == 32 * 7) {
return
_makeCalldataCall(token, IERC20Permit.permit.selector, permit);
}
if (permit.length == 32 * 8) {
return
_makeCalldataCall(
token,
IDaiLikePermit.permit.selector,
permit
);
}
revert SafePermitBadLength();
}
function _makeCall(
IERC20 token,
bytes4 selector,
address to,
uint256 amount
) private returns (bool success) {
assembly {
let data := mload(0x40)
mstore(data, selector)
mstore(add(data, 0x04), to)
mstore(add(data, 0x24), amount)
success := call(gas(), token, 0, data, 0x44, 0x0, 0x20)
if success {
switch returndatasize()
case 0 {
success := gt(extcodesize(token), 0)
}
default {
success := and(gt(returndatasize(), 31), eq(mload(0), 1))
}
}
}
}
function _makeCalldataCall(
IERC20 token,
bytes4 selector,
bytes calldata args
) private returns (bool success) {
assembly {
let len := add(4, args.length)
let data := mload(0x40)
mstore(data, selector)
calldatacopy(add(data, 0x04), args.offset, args.length)
success := call(gas(), token, 0, data, len, 0x0, 0x20)
if success {
switch returndatasize()
case 0 {
success := gt(extcodesize(token), 0)
}
default {
success := and(gt(returndatasize(), 31), eq(mload(0), 1))
}
}
}
}
}
文件 18 的 20:StringUtil.sol
pragma solidity ^0.8.0;
pragma abicoder v1;
library StringUtil {
function toHex(uint256 value) internal pure returns (string memory) {
return toHex(abi.encodePacked(value));
}
function toHex(address value) internal pure returns (string memory) {
return toHex(abi.encodePacked(value));
}
function toHex(
bytes memory data
) internal pure returns (string memory result) {
assembly {
function _toHex16(input) -> output {
output := or(
and(
input,
0xFFFFFFFFFFFFFFFF000000000000000000000000000000000000000000000000
),
shr(
64,
and(
input,
0x0000000000000000FFFFFFFFFFFFFFFF00000000000000000000000000000000
)
)
)
output := or(
and(
output,
0xFFFFFFFF000000000000000000000000FFFFFFFF000000000000000000000000
),
shr(
32,
and(
output,
0x00000000FFFFFFFF000000000000000000000000FFFFFFFF0000000000000000
)
)
)
output := or(
and(
output,
0xFFFF000000000000FFFF000000000000FFFF000000000000FFFF000000000000
),
shr(
16,
and(
output,
0x0000FFFF000000000000FFFF000000000000FFFF000000000000FFFF00000000
)
)
)
output := or(
and(
output,
0xFF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000
),
shr(
8,
and(
output,
0x00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
)
)
)
output := or(
shr(
4,
and(
output,
0xF000F000F000F000F000F000F000F000F000F000F000F000F000F000F000F000
)
),
shr(
8,
and(
output,
0x0F000F000F000F000F000F000F000F000F000F000F000F000F000F000F000F00
)
)
)
output := add(
add(
0x3030303030303030303030303030303030303030303030303030303030303030,
output
),
mul(
and(
shr(
4,
add(
output,
0x0606060606060606060606060606060606060606060606060606060606060606
)
),
0x0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F
),
7
)
)
}
result := mload(0x40)
let length := mload(data)
let resultLength := shl(1, length)
let toPtr := add(result, 0x22)
mstore(0x40, add(toPtr, resultLength))
mstore(add(result, 2), 0x3078)
mstore(result, add(resultLength, 2))
for {
let fromPtr := add(data, 0x20)
let endPtr := add(fromPtr, length)
} lt(fromPtr, endPtr) {
fromPtr := add(fromPtr, 0x20)
} {
let rawData := mload(fromPtr)
let hexData := _toHex16(rawData)
mstore(toPtr, hexData)
toPtr := add(toPtr, 0x20)
hexData := _toHex16(shl(128, rawData))
mstore(toPtr, hexData)
toPtr := add(toPtr, 0x20)
}
}
}
}
文件 19 的 20:UniERC20.sol
pragma solidity ^0.8.0;
pragma abicoder v1;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "../interfaces/IERC20MetadataUppercase.sol";
import "./SafeERC20.sol";
import "./StringUtil.sol";
library UniERC20 {
using SafeERC20 for IERC20;
error InsufficientBalance();
error ApproveCalledOnETH();
error NotEnoughValue();
error FromIsNotSender();
error ToIsNotThis();
error ETHTransferFailed();
uint256 private constant _RAW_CALL_GAS_LIMIT = 5000;
IERC20 private constant _ETH_ADDRESS =
IERC20(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
IERC20 private constant _ZERO_ADDRESS = IERC20(address(0));
function isETH(IERC20 token) internal pure returns (bool) {
return (token == _ZERO_ADDRESS || token == _ETH_ADDRESS);
}
function uniBalanceOf(
IERC20 token,
address account
) internal view returns (uint256) {
if (isETH(token)) {
return account.balance;
} else {
return token.balanceOf(account);
}
}
function uniTransfer(
IERC20 token,
address payable to,
uint256 amount
) internal {
if (amount > 0) {
if (isETH(token)) {
if (address(this).balance < amount)
revert InsufficientBalance();
(bool success, ) = to.call{
value: amount,
gas: _RAW_CALL_GAS_LIMIT
}("");
if (!success) revert ETHTransferFailed();
} else {
token.safeTransfer(to, amount);
}
}
}
function uniTransferFrom(
IERC20 token,
address payable from,
address to,
uint256 amount
) internal {
if (amount > 0) {
if (isETH(token)) {
if (msg.value < amount) revert NotEnoughValue();
if (from != msg.sender) revert FromIsNotSender();
if (to != address(this)) revert ToIsNotThis();
if (msg.value > amount) {
unchecked {
(bool success, ) = from.call{
value: msg.value - amount,
gas: _RAW_CALL_GAS_LIMIT
}("");
if (!success) revert ETHTransferFailed();
}
}
} else {
token.safeTransferFrom(from, to, amount);
}
}
}
function uniSymbol(IERC20 token) internal view returns (string memory) {
return
_uniDecode(
token,
IERC20Metadata.symbol.selector,
IERC20MetadataUppercase.SYMBOL.selector
);
}
function uniName(IERC20 token) internal view returns (string memory) {
return
_uniDecode(
token,
IERC20Metadata.name.selector,
IERC20MetadataUppercase.NAME.selector
);
}
function uniApprove(IERC20 token, address to, uint256 amount) internal {
if (isETH(token)) revert ApproveCalledOnETH();
token.forceApprove(to, amount);
}
function _uniDecode(
IERC20 token,
bytes4 lowerCaseSelector,
bytes4 upperCaseSelector
) private view returns (string memory result) {
if (isETH(token)) {
return "ETH";
}
(bool success, bytes memory data) = address(token).staticcall{
gas: 20000
}(abi.encodeWithSelector(lowerCaseSelector));
if (!success) {
(success, data) = address(token).staticcall{gas: 20000}(
abi.encodeWithSelector(upperCaseSelector)
);
}
if (success && data.length >= 0x40) {
(uint256 offset, uint256 len) = abi.decode(
data,
(uint256, uint256)
);
if (offset == 0x20 && data.length >= 0x40 + len) {
assembly {
result := add(data, 0x40)
}
return result;
}
}
if (success && data.length == 32) {
uint256 len = 0;
while (
len < data.length && data[len] >= 0x20 && data[len] <= 0x7E
) {
unchecked {
len++;
}
}
if (len > 0) {
assembly {
mstore(data, len)
}
return string(data);
}
}
return StringUtil.toHex(address(token));
}
}
文件 20 的 20: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/ArbswapSmartRouter.sol": "ArbswapSmartRouter"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_WETHAddress","type":"address"},{"internalType":"address","name":"_arbswapV2","type":"address"},{"internalType":"address","name":"_stableswapFactory","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ApproveCalledOnETH","type":"error"},{"inputs":[],"name":"ETHTransferFailed","type":"error"},{"inputs":[],"name":"ForceApproveFailed","type":"error"},{"inputs":[],"name":"FromIsNotSender","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"NotEnoughValue","type":"error"},{"inputs":[],"name":"SafeTransferFailed","type":"error"},{"inputs":[],"name":"SafeTransferFromFailed","type":"error"},{"inputs":[],"name":"ToIsNotThis","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"factory","type":"address"}],"name":"NewStableSwapFactory","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":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"srcTokenAddr","type":"address"},{"indexed":true,"internalType":"address","name":"dstTokenAddr","type":"address"},{"indexed":false,"internalType":"uint256","name":"srcAmount","type":"uint256"}],"name":"Swap","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"srcTokenAddr","type":"address"},{"indexed":true,"internalType":"address","name":"dstTokenAddr","type":"address"},{"indexed":false,"internalType":"uint256","name":"srcAmount","type":"uint256"}],"name":"SwapMulti","type":"event"},{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[],"name":"arbswapV2","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_factory","type":"address"}],"name":"setStableSwapFactory","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stableswapFactory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"srcToken","type":"address"},{"internalType":"contract IERC20","name":"dstToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"minReturn","type":"uint256"},{"internalType":"enum ArbswapSmartRouter.FLAG","name":"flag","type":"uint8"}],"name":"swap","outputs":[{"internalType":"uint256","name":"returnAmount","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract IERC20[]","name":"tokens","type":"address[]"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"minReturn","type":"uint256"},{"internalType":"enum ArbswapSmartRouter.FLAG[]","name":"flags","type":"uint8[]"}],"name":"swapMulti","outputs":[{"internalType":"uint256","name":"returnAmount","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"weth","outputs":[{"internalType":"contract IWETH02","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]