编译器
0.8.17+commit.8df45f5f
文件 1 的 16:AccessControl.sol
pragma solidity ^0.8.0;
import "./IAccessControl.sol";
import "../utils/Context.sol";
import "../utils/Strings.sol";
import "../utils/introspection/ERC165.sol";
abstract contract AccessControl is Context, IAccessControl, ERC165 {
struct RoleData {
mapping(address => bool) members;
bytes32 adminRole;
}
mapping(bytes32 => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
return _roles[role].members[account];
}
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert(
string(
abi.encodePacked(
"AccessControl: account ",
Strings.toHexString(uint160(account), 20),
" is missing role ",
Strings.toHexString(uint256(role), 32)
)
)
);
}
}
function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
return _roles[role].adminRole;
}
function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
function renounceRole(bytes32 role, address account) public virtual override {
require(account == _msgSender(), "AccessControl: can only renounce roles for self");
_revokeRole(role, account);
}
function _setupRole(bytes32 role, address account) internal virtual {
_grantRole(role, account);
}
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
bytes32 previousAdminRole = getRoleAdmin(role);
_roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
function _grantRole(bytes32 role, address account) internal virtual {
if (!hasRole(role, account)) {
_roles[role].members[account] = true;
emit RoleGranted(role, account, _msgSender());
}
}
function _revokeRole(bytes32 role, address account) internal virtual {
if (hasRole(role, account)) {
_roles[role].members[account] = false;
emit RoleRevoked(role, account, _msgSender());
}
}
}
文件 2 的 16:CamelotYakRouter.sol
pragma solidity ^0.8.17;
pragma experimental ABIEncoderV2;
import "./interface/IYakRouter.sol";
import "./interface/IAdapter.sol";
import "./interface/IERC20.sol";
import "./interface/IWETH.sol";
import "./lib/SafeERC20.sol";
import "./lib/Maintainable.sol";
import "./lib/YakViewUtils.sol";
import "./lib/Recoverable.sol";
import "./lib/SafeERC20.sol";
contract CamelotYakRouter is Maintainable, Recoverable, IYakRouter {
using SafeERC20 for IERC20;
using OfferUtils for Offer;
address public immutable WNATIVE;
address public constant NATIVE = address(0);
string public constant NAME = "CamelotYakRouter";
uint256 public constant FEE_DENOMINATOR = 1e4;
uint256 public MIN_FEE = 0;
address public FEE_CLAIMER;
address[] public TRUSTED_TOKENS;
address[] public ADAPTERS;
constructor(
address[] memory _adapters,
address[] memory _trustedTokens,
address _feeClaimer,
address _wrapped_native
) {
setAllowanceForWrapping(_wrapped_native);
setTrustedTokens(_trustedTokens);
setFeeClaimer(_feeClaimer);
setAdapters(_adapters);
WNATIVE = _wrapped_native;
}
function setAllowanceForWrapping(address _wnative) public onlyMaintainer {
IERC20(_wnative).safeApprove(_wnative, type(uint256).max);
}
function setTrustedTokens(address[] memory _trustedTokens) override public onlyMaintainer {
emit UpdatedTrustedTokens(_trustedTokens);
TRUSTED_TOKENS = _trustedTokens;
}
function setAdapters(address[] memory _adapters) override public onlyMaintainer {
emit UpdatedAdapters(_adapters);
ADAPTERS = _adapters;
}
function setMinFee(uint256 _fee) override external onlyMaintainer {
emit UpdatedMinFee(MIN_FEE, _fee);
MIN_FEE = _fee;
}
function setFeeClaimer(address _claimer) override public onlyMaintainer {
emit UpdatedFeeClaimer(FEE_CLAIMER, _claimer);
FEE_CLAIMER = _claimer;
}
function trustedTokensCount() override external view returns (uint256) {
return TRUSTED_TOKENS.length;
}
function adaptersCount() override external view returns (uint256) {
return ADAPTERS.length;
}
receive() external payable {}
function _applyFee(uint256 _amountIn, uint256 _fee) internal view returns (uint256) {
require(_fee >= MIN_FEE, "YakRouter: Insufficient fee");
return (_amountIn * (FEE_DENOMINATOR - _fee)) / FEE_DENOMINATOR;
}
function _wrap(uint256 _amount) internal {
IWETH(WNATIVE).deposit{ value: _amount }();
}
function _unwrap(uint256 _amount) internal {
IWETH(WNATIVE).withdraw(_amount);
}
function _unwrapTo(address _to, uint256 _amount) internal {
IWETH(WNATIVE).withdrawTo(_to, _amount);
}
function _returnTokensTo(
address _token,
uint256 _amount,
address _to
) internal {
if (address(this) != _to) {
if (_token == NATIVE) {
payable(_to).transfer(_amount);
} else {
IERC20(_token).safeTransfer(_to, _amount);
}
}
}
function _transferFrom(address token, address _from, address _to, uint _amount) internal {
if (_from != address(this))
IERC20(token).safeTransferFrom(_from, _to, _amount);
else
IERC20(token).safeTransfer(_to, _amount);
}
function queryAdapter(
uint256 _amountIn,
address _tokenIn,
address _tokenOut,
uint8 _index
) override external view returns (uint256, address) {
IAdapter _adapter = IAdapter(ADAPTERS[_index]);
try IAdapter(_adapter).query(_amountIn, _tokenIn, _tokenOut) returns (uint256 _amountOut, address _recipient) {
return (_amountOut, _recipient);
}
catch { return (0, address(0)); }
}
function queryNoSplit(
uint256 _amountIn,
address _tokenIn,
address _tokenOut,
uint8[] calldata _options
) override public view returns (Query memory) {
Query memory bestQuery;
for (uint8 i; i < _options.length; i++) {
address _adapter = ADAPTERS[_options[i]];
try IAdapter(_adapter).query(_amountIn, _tokenIn, _tokenOut) returns (uint256 amountOut, address _recipient) {
if (i == 0 || amountOut > bestQuery.amountOut) {
bestQuery = Query(_adapter, _recipient, _tokenIn, _tokenOut, amountOut);
}
}
catch { continue; }
}
return bestQuery;
}
function queryNoSplit(
uint256 _amountIn,
address _tokenIn,
address _tokenOut
) override public view returns (Query memory) {
Query memory bestQuery;
for (uint8 i; i < ADAPTERS.length; i++) {
address _adapter = ADAPTERS[i];
try IAdapter(_adapter).query(_amountIn, _tokenIn, _tokenOut) returns (uint256 amountOut, address _recipient) {
if (i == 0 || amountOut > bestQuery.amountOut) {
bestQuery = Query(_adapter, _recipient, _tokenIn, _tokenOut, amountOut);
}
}
catch { continue; }
}
return bestQuery;
}
function findBestPathWithGas(
uint256 _amountIn,
address _tokenIn,
address _tokenOut,
address[] memory _trustedTokens,
uint256 _maxSteps,
uint256 _gasPrice
) override external view returns (FormattedOffer memory) {
require(_maxSteps > 0 && _maxSteps < 5, "YakRouter: Invalid max-steps");
Offer memory queries = OfferUtils.newOffer(_amountIn, _tokenIn);
uint256 gasPriceInExitTkn = _gasPrice > 0 ? getGasPriceInExitTkn(_gasPrice, _tokenOut) : 0;
uint256 ttLength = TRUSTED_TOKENS.length;
address[] memory _allTrustedTokens = new address[](
ttLength + _trustedTokens.length
);
for (uint i = 0; i < ttLength; ) {
_allTrustedTokens[i] = TRUSTED_TOKENS[i];
unchecked {
i++;
}
}
for (uint i = 0; i < _trustedTokens.length; ) {
_allTrustedTokens[ttLength + i] = _trustedTokens[i];
unchecked {
i++;
}
}
queries = _findBestPath(_amountIn, _tokenIn, _tokenOut, _allTrustedTokens, _maxSteps, queries, gasPriceInExitTkn);
if (queries.adapters.length == 0) {
queries.amounts = "";
queries.path = "";
}
return queries.format();
}
function getGasPriceInExitTkn(uint256 _gasPrice, address _tokenOut) internal view returns (uint256 price) {
address[] memory _trustedTokens;
FormattedOffer memory gasQuery = findBestPath(1e18, WNATIVE, _tokenOut, _trustedTokens, 2);
if (gasQuery.path.length != 0) {
price = (gasQuery.amounts[gasQuery.amounts.length - 1] * _gasPrice) / 1e9;
}
}
function findBestPath(
uint256 _amountIn,
address _tokenIn,
address _tokenOut,
address[] memory _trustedTokens,
uint256 _maxSteps
) override public view returns (FormattedOffer memory) {
require(_maxSteps > 0 && _maxSteps < 5, "YakRouter: Invalid max-steps");
Offer memory queries = OfferUtils.newOffer(_amountIn, _tokenIn);
uint256 ttLength = TRUSTED_TOKENS.length;
address[] memory _allTrustedTokens = new address[](
ttLength + _trustedTokens.length
);
for (uint i = 0; i < ttLength; ) {
_allTrustedTokens[i] = TRUSTED_TOKENS[i];
unchecked {
i++;
}
}
for (uint i = 0; i < _trustedTokens.length; ) {
_allTrustedTokens[ttLength + i] = _trustedTokens[i];
unchecked {
i++;
}
}
queries = _findBestPath(_amountIn, _tokenIn, _tokenOut, _allTrustedTokens, _maxSteps, queries, 0);
if (queries.adapters.length == 0) {
queries.amounts = "";
queries.path = "";
}
return queries.format();
}
function _findBestPath(
uint256 _amountIn,
address _tokenIn,
address _tokenOut,
address[] memory _trustedTokens,
uint256 _maxSteps,
Offer memory _queries,
uint256 _tknOutPriceNwei
) internal view returns (Offer memory) {
Offer memory bestOption = _queries.clone();
uint256 bestAmountOut;
uint256 gasEstimate;
bool withGas = _tknOutPriceNwei > 0;
Query memory queryDirect = queryNoSplit(_amountIn, _tokenIn, _tokenOut);
if (queryDirect.amountOut > 0) {
if (withGas) {
gasEstimate = IAdapter(queryDirect.adapter).swapGasEstimate();
}
bestOption.addToTail(queryDirect.amountOut, queryDirect.adapter, queryDirect.recipient, queryDirect.tokenOut, gasEstimate);
bestAmountOut = queryDirect.amountOut;
}
if (_maxSteps > 1 && _queries.adapters.length / 32 <= _maxSteps - 2) {
for (uint256 i = 0; i < _trustedTokens.length; ) {
if (_tokenIn == _trustedTokens[i]) {
unchecked {
i++;
}
continue;
}
Query memory bestSwap = queryNoSplit(_amountIn, _tokenIn, _trustedTokens[i]);
if (bestSwap.amountOut == 0) {
unchecked {
i++;
}
continue;
}
Offer memory newOffer = _queries.clone();
if (withGas) {
gasEstimate = IAdapter(bestSwap.adapter).swapGasEstimate();
}
newOffer.addToTail(bestSwap.amountOut, bestSwap.adapter, bestSwap.recipient, bestSwap.tokenOut, gasEstimate);
newOffer = _findBestPath(
bestSwap.amountOut,
_trustedTokens[i],
_tokenOut,
_trustedTokens,
_maxSteps,
newOffer,
_tknOutPriceNwei
);
if (
_tokenOut == newOffer.getTokenOut() &&
newOffer.getAmountOut() > bestAmountOut
) {
if (newOffer.gasEstimate > bestOption.gasEstimate) {
uint256 gasCostDiff = (_tknOutPriceNwei * (newOffer.gasEstimate - bestOption.gasEstimate)) /
1e9;
if (
gasCostDiff >
newOffer.getAmountOut() - bestAmountOut
) {
unchecked {
i++;
}
continue;
}
}
bestAmountOut = newOffer.getAmountOut();
bestOption = newOffer;
}
unchecked {
i++;
}
}
}
return bestOption;
}
function _swapNoSplit(
Trade calldata _trade,
address _from,
uint256 _fee,
address _to
) internal returns (uint256) {
uint256 amountIn = _trade.amountIn;
if (_fee > 0 || MIN_FEE > 0) {
amountIn = _applyFee(_trade.amountIn, _fee);
_transferFrom(_trade.path[0], _from, FEE_CLAIMER, _trade.amountIn - amountIn);
}
uint256 recipientBalanceBefore = IERC20(_trade.path[0]).balanceOf(_trade.recipients[0]);
_transferFrom(_trade.path[0], _from, _trade.recipients[0], amountIn);
amountIn = IERC20(_trade.path[0]).balanceOf(_trade.recipients[0]) - recipientBalanceBefore;
address tokenOut = _trade.path[_trade.path.length - 1];
for (uint256 i = 0; i < _trade.adapters.length; i++) {
address targetAddress = i < _trade.adapters.length - 1 ? _trade.recipients[i + 1] : _to;
recipientBalanceBefore = IERC20(_trade.path[i + 1]).balanceOf(targetAddress);
IAdapter(_trade.adapters[i]).swap(
amountIn,
0,
_trade.path[i],
_trade.path[i + 1],
targetAddress
);
amountIn = IERC20(_trade.path[i + 1]).balanceOf(targetAddress) - recipientBalanceBefore;
}
uint256 amountOut = amountIn;
require(amountOut >= _trade.amountOut, "YakRouter: Insufficient output amount");
emit YakSwap(_trade.path[0], tokenOut, _trade.amountIn, amountOut);
return amountOut;
}
function swapNoSplit(
Trade calldata _trade,
uint256 _fee,
address _to
) override public {
_swapNoSplit(_trade, msg.sender, _fee, _to);
}
function swapNoSplitFromETH(
Trade calldata _trade,
uint256 _fee,
address _to
) override external payable {
require(_trade.path[0] == WNATIVE, "YakRouter: Path needs to begin with WETH");
_wrap(_trade.amountIn);
_swapNoSplit(_trade, address(this), _fee, _to);
}
function swapNoSplitToETH(
Trade calldata _trade,
uint256 _fee,
address _to
) override public {
require(_trade.path[_trade.path.length - 1] == WNATIVE, "YakRouter: Path needs to end with WETH");
uint256 returnAmount = _swapNoSplit(_trade, msg.sender, _fee, address(this));
_unwrapTo(_to, returnAmount);
}
function swapNoSplitWithPermit(
Trade calldata _trade,
uint256 _fee,
address _to,
uint256 _deadline,
uint8 _v,
bytes32 _r,
bytes32 _s
) override external {
IERC20(_trade.path[0]).permit(msg.sender, address(this), _trade.amountIn, _deadline, _v, _r, _s);
swapNoSplit(_trade, _fee, _to);
}
function swapNoSplitToETHWithPermit(
Trade calldata _trade,
uint256 _fee,
address _to,
uint256 _deadline,
uint8 _v,
bytes32 _r,
bytes32 _s
) override external {
IERC20(_trade.path[0]).permit(msg.sender, address(this), _trade.amountIn, _deadline, _v, _r, _s);
swapNoSplitToETH(_trade, _fee, _to);
}
}
文件 3 的 16: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 的 16:ERC165.sol
pragma solidity ^0.8.0;
import "./IERC165.sol";
abstract contract ERC165 is IERC165 {
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
文件 5 的 16:IAccessControl.sol
pragma solidity ^0.8.0;
interface IAccessControl {
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
function hasRole(bytes32 role, address account) external view returns (bool);
function getRoleAdmin(bytes32 role) external view returns (bytes32);
function grantRole(bytes32 role, address account) external;
function revokeRole(bytes32 role, address account) external;
function renounceRole(bytes32 role, address account) external;
}
文件 6 的 16:IAdapter.sol
pragma solidity ^0.8.0;
interface IAdapter {
function name() external view returns (string memory);
function swapGasEstimate() external view returns (uint256);
function swap(
uint256,
uint256,
address,
address,
address
) external;
function query(
uint256,
address,
address
) external view returns (uint256, address);
}
文件 7 的 16:IERC165.sol
pragma solidity ^0.8.0;
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
文件 8 的 16:IERC20.sol
pragma solidity ^0.8.0;
interface IERC20 {
event Approval(address, address, uint256);
event Transfer(address, address, uint256);
function name() external view returns (string memory);
function decimals() external view returns (uint8);
function transferFrom(
address,
address,
uint256
) external returns (bool);
function allowance(address, address) external view returns (uint256);
function approve(address, uint256) external returns (bool);
function transfer(address, uint256) external returns (bool);
function balanceOf(address) external view returns (uint256);
function nonces(address) external view returns (uint256);
function permit(
address,
address,
uint256,
uint256,
uint8,
bytes32,
bytes32
) external;
function swap(address, uint256) external;
function swapSupply(address) external view returns (uint256);
function totalSupply() external view returns (uint256);
}
文件 9 的 16:IWETH.sol
pragma solidity ^0.8.0;
import "./IERC20.sol";
interface IWETH is IERC20 {
function withdraw(uint256 amount) external;
function withdrawTo(address account, uint256 amount) external;
function deposit() external payable;
}
文件 10 的 16:IYakRouter.sol
pragma solidity ^0.8.0;
struct Query {
address adapter;
address recipient;
address tokenIn;
address tokenOut;
uint256 amountOut;
}
struct Offer {
bytes amounts;
bytes adapters;
bytes path;
bytes recipients;
uint256 gasEstimate;
}
struct FormattedOffer {
uint256[] amounts;
address[] adapters;
address[] path;
address[] recipients;
uint256 gasEstimate;
}
struct Trade {
uint256 amountIn;
uint256 amountOut;
address[] path;
address[] adapters;
address[] recipients;
}
interface IYakRouter {
event UpdatedTrustedTokens(address[] _newTrustedTokens);
event UpdatedAdapters(address[] _newAdapters);
event UpdatedMinFee(uint256 _oldMinFee, uint256 _newMinFee);
event UpdatedFeeClaimer(address _oldFeeClaimer, address _newFeeClaimer);
event YakSwap(address indexed _tokenIn, address indexed _tokenOut, uint256 _amountIn, uint256 _amountOut);
function setTrustedTokens(address[] memory _trustedTokens) external;
function setAdapters(address[] memory _adapters) external;
function setFeeClaimer(address _claimer) external;
function setMinFee(uint256 _fee) external;
function trustedTokensCount() external view returns (uint256);
function adaptersCount() external view returns (uint256);
function queryAdapter(
uint256 _amountIn,
address _tokenIn,
address _tokenOut,
uint8 _index
) external returns (uint256, address);
function queryNoSplit(
uint256 _amountIn,
address _tokenIn,
address _tokenOut,
uint8[] calldata _options
) external view returns (Query memory);
function queryNoSplit(
uint256 _amountIn,
address _tokenIn,
address _tokenOut
) external view returns (Query memory);
function findBestPathWithGas(
uint256 _amountIn,
address _tokenIn,
address _tokenOut,
address[] memory _trustedTokens,
uint256 _maxSteps,
uint256 _gasPrice
) external view returns (FormattedOffer memory);
function findBestPath(
uint256 _amountIn,
address _tokenIn,
address _tokenOut,
address[] memory _trustedTokens,
uint256 _maxSteps
) external view returns (FormattedOffer memory);
function swapNoSplit(
Trade calldata _trade,
uint256 _fee,
address _to
) external;
function swapNoSplitFromETH(
Trade calldata _trade,
uint256 _fee,
address _to
) external payable;
function swapNoSplitToETH(
Trade calldata _trade,
uint256 _fee,
address _to
) external;
function swapNoSplitWithPermit(
Trade calldata _trade,
uint256 _fee,
address _to,
uint256 _deadline,
uint8 _v,
bytes32 _r,
bytes32 _s
) external;
function swapNoSplitToETHWithPermit(
Trade calldata _trade,
uint256 _fee,
address _to,
uint256 _deadline,
uint8 _v,
bytes32 _r,
bytes32 _s
) external;
}
文件 11 的 16:Maintainable.sol
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/AccessControl.sol";
abstract contract Maintainable is Context, AccessControl {
bytes32 public constant MAINTAINER_ROLE = keccak256("MAINTAINER_ROLE");
constructor() {
address msgSender = _msgSender();
_setupRole(DEFAULT_ADMIN_ROLE, msgSender);
_setupRole(MAINTAINER_ROLE, msgSender);
}
function addMaintainer(address addedMaintainer) public virtual {
grantRole(MAINTAINER_ROLE, addedMaintainer);
}
function removeMaintainer(address removedMaintainer) public virtual {
revokeRole(MAINTAINER_ROLE, removedMaintainer);
}
function renounceRole(bytes32 role) public virtual {
address msgSender = _msgSender();
renounceRole(role, msgSender);
}
function transferOwnership(address newOwner) public virtual {
address msgSender = _msgSender();
grantRole(DEFAULT_ADMIN_ROLE, newOwner);
renounceRole(DEFAULT_ADMIN_ROLE, msgSender);
}
modifier onlyMaintainer() {
address msgSender = _msgSender();
require(hasRole(MAINTAINER_ROLE, msgSender), "Maintainable: Caller is not a maintainer");
_;
}
}
文件 12 的 16:Recoverable.sol
pragma solidity >=0.8.0;
import "./SafeERC20.sol";
import "./Maintainable.sol";
abstract contract Recoverable is Maintainable {
using SafeERC20 for IERC20;
event Recovered(
address indexed _asset,
uint amount
);
function recoverERC20(address _tokenAddress, uint _tokenAmount) external onlyMaintainer {
require(_tokenAmount > 0, "Nothing to recover");
IERC20(_tokenAddress).safeTransfer(msg.sender, _tokenAmount);
emit Recovered(_tokenAddress, _tokenAmount);
}
function recoverNative(uint _amount) external onlyMaintainer {
require(_amount > 0, "Nothing to recover");
payable(msg.sender).transfer(_amount);
emit Recovered(address(0), _amount);
}
}
文件 13 的 16:SafeERC20.sol
pragma solidity ^0.8.0;
pragma experimental ABIEncoderV2;
import "../interface/IERC20.sol";
library SafeERC20 {
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 _callOptionalReturn(IERC20 token, bytes memory data) private {
(bool success, bytes memory returndata) = address(token).call(data);
require(success, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
文件 14 的 16:Strings.sol
pragma solidity ^0.8.0;
library Strings {
bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
uint8 private constant _ADDRESS_LENGTH = 20;
function toString(uint256 value) internal pure returns (string memory) {
if (value == 0) {
return "0";
}
uint256 temp = value;
uint256 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
digits -= 1;
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
return string(buffer);
}
function toHexString(uint256 value) internal pure returns (string memory) {
if (value == 0) {
return "0x00";
}
uint256 temp = value;
uint256 length = 0;
while (temp != 0) {
length++;
temp >>= 8;
}
return toHexString(value, length);
}
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _HEX_SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
}
文件 15 的 16:TypeConversion.sol
pragma solidity >=0.8.0;
library TypeConversion {
function toBytes12(address x) internal pure returns (bytes12 y) {
assembly { y := x }
}
function toBytes32(address x) internal pure returns (bytes32 y) {
assembly { y := x }
}
function toAddress(bytes32 x) internal pure returns (address y) {
assembly { y := x }
}
function toBytes(address x) internal pure returns (bytes memory y) {
y = new bytes(32);
assembly { mstore(add(y, 32), x) }
}
function toBytes(bytes32 x) internal pure returns (bytes memory y) {
y = new bytes(32);
assembly { mstore(add(y, 32), x) }
}
function toBytes(uint x) internal pure returns (bytes memory y) {
y = new bytes(32);
assembly { mstore(add(y, 32), x) }
}
function toAddress(
bytes memory x,
uint offset
) internal pure returns (address y) {
assembly { y := mload(add(x, offset)) }
}
function toUint(
bytes memory x,
uint offset
) internal pure returns (uint y) {
assembly { y := mload(add(x, offset)) }
}
function toBytes12(
bytes memory x,
uint offset
) internal pure returns (bytes12 y) {
assembly { y := mload(add(x, offset)) }
}
function toBytes32(
bytes memory x,
uint offset
) internal pure returns (bytes32 y) {
assembly { y := mload(add(x, offset)) }
}
function toAddresses(
bytes memory xs
) internal pure returns (address[] memory ys) {
ys = new address[](xs.length/32);
for (uint i=0; i < xs.length/32; i++) {
ys[i] = toAddress(xs, i*32 + 32);
}
}
function toUints(
bytes memory xs
) internal pure returns (uint[] memory ys) {
ys = new uint[](xs.length/32);
for (uint i=0; i < xs.length/32; i++) {
ys[i] = toUint(xs, i*32 + 32);
}
}
function toBytes32s(
bytes memory xs
) internal pure returns (bytes32[] memory ys) {
ys = new bytes32[](xs.length/32);
for (uint i=0; i < xs.length/32; i++) {
ys[i] = toBytes32(xs, i*32 + 32);
}
}
}
文件 16 的 16:YakViewUtils.sol
pragma solidity >=0.8.4;
import { Offer, FormattedOffer } from "../interface/IYakRouter.sol";
import "./TypeConversion.sol";
library OfferUtils {
using TypeConversion for address;
using TypeConversion for uint256;
using TypeConversion for bytes;
function newOffer(
uint _amountIn,
address _tokenIn
) internal pure returns (Offer memory offer) {
offer.amounts = _amountIn.toBytes();
offer.path = _tokenIn.toBytes();
}
function clone(Offer memory _queries) internal pure returns (Offer memory) {
return Offer(_queries.amounts, _queries.adapters, _queries.path, _queries.recipients, _queries.gasEstimate);
}
function addToTail(
Offer memory _queries,
uint256 _amount,
address _adapter,
address _recipient,
address _tokenOut,
uint256 _gasEstimate
) internal pure {
_queries.path = bytes.concat(_queries.path, _tokenOut.toBytes());
_queries.adapters = bytes.concat(_queries.adapters, _adapter.toBytes());
_queries.amounts = bytes.concat(_queries.amounts, _amount.toBytes());
_queries.recipients = bytes.concat(_queries.recipients, _recipient.toBytes());
_queries.gasEstimate += _gasEstimate;
}
function format(Offer memory _queries) internal pure returns (FormattedOffer memory) {
return
FormattedOffer(
_queries.amounts.toUints(),
_queries.adapters.toAddresses(),
_queries.path.toAddresses(),
_queries.recipients.toAddresses(),
_queries.gasEstimate
);
}
function getTokenOut(
Offer memory _offer
) internal pure returns (address tokenOut) {
tokenOut = _offer.path.toAddress(_offer.path.length);
}
function getAmountOut(
Offer memory _offer
) internal pure returns (uint amountOut) {
amountOut = _offer.amounts.toUint(_offer.path.length);
}
}
library FormattedOfferUtils {
using TypeConversion for address;
using TypeConversion for uint256;
using TypeConversion for bytes;
function addToTail(
FormattedOffer memory offer,
uint256 amountOut,
address wrapper,
address tokenOut,
address recipient,
uint256 gasEstimate
) internal pure {
offer.amounts = bytes.concat(abi.encodePacked(offer.amounts), amountOut.toBytes()).toUints();
offer.adapters = bytes.concat(abi.encodePacked(offer.adapters), wrapper.toBytes()).toAddresses();
offer.path = bytes.concat(abi.encodePacked(offer.path), tokenOut.toBytes()).toAddresses();
offer.recipients = bytes.concat(abi.encodePacked(offer.recipients), recipient.toBytes()).toAddresses();
offer.gasEstimate += gasEstimate;
}
function addToHead(
FormattedOffer memory offer,
uint256 amountOut,
address wrapper,
address tokenOut,
address recipient,
uint256 gasEstimate
) internal pure {
offer.amounts = bytes.concat(amountOut.toBytes(), abi.encodePacked(offer.amounts)).toUints();
offer.adapters = bytes.concat(wrapper.toBytes(), abi.encodePacked(offer.adapters)).toAddresses();
offer.path = bytes.concat(tokenOut.toBytes(), abi.encodePacked(offer.path)).toAddresses();
offer.path = bytes.concat(recipient.toBytes(), abi.encodePacked(offer.recipients)).toAddresses();
offer.gasEstimate += gasEstimate;
}
function getAmountOut(FormattedOffer memory offer) internal pure returns (uint256) {
return offer.amounts[offer.amounts.length - 1];
}
}
{
"compilationTarget": {
"src/contracts/CamelotYakRouter.sol": "CamelotYakRouter"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"details": {
"constantOptimizer": true,
"cse": true,
"deduplicate": true,
"inliner": true,
"jumpdestRemover": true,
"orderLiterals": true,
"peephole": true,
"yul": true,
"yulDetails": {
"optimizerSteps": "u",
"stackAllocation": true
}
},
"runs": 10000
},
"remappings": [],
"viaIR": true
}
[{"inputs":[{"internalType":"address[]","name":"_adapters","type":"address[]"},{"internalType":"address[]","name":"_trustedTokens","type":"address[]"},{"internalType":"address","name":"_feeClaimer","type":"address"},{"internalType":"address","name":"_wrapped_native","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Recovered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"_newAdapters","type":"address[]"}],"name":"UpdatedAdapters","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_oldFeeClaimer","type":"address"},{"indexed":false,"internalType":"address","name":"_newFeeClaimer","type":"address"}],"name":"UpdatedFeeClaimer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_oldMinFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_newMinFee","type":"uint256"}],"name":"UpdatedMinFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"_newTrustedTokens","type":"address[]"}],"name":"UpdatedTrustedTokens","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_tokenIn","type":"address"},{"indexed":true,"internalType":"address","name":"_tokenOut","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amountIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_amountOut","type":"uint256"}],"name":"YakSwap","type":"event"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"ADAPTERS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FEE_CLAIMER","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FEE_DENOMINATOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAINTAINER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NAME","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NATIVE","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"TRUSTED_TOKENS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WNATIVE","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"adaptersCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addedMaintainer","type":"address"}],"name":"addMaintainer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amountIn","type":"uint256"},{"internalType":"address","name":"_tokenIn","type":"address"},{"internalType":"address","name":"_tokenOut","type":"address"},{"internalType":"address[]","name":"_trustedTokens","type":"address[]"},{"internalType":"uint256","name":"_maxSteps","type":"uint256"}],"name":"findBestPath","outputs":[{"components":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"address[]","name":"adapters","type":"address[]"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address[]","name":"recipients","type":"address[]"},{"internalType":"uint256","name":"gasEstimate","type":"uint256"}],"internalType":"struct FormattedOffer","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amountIn","type":"uint256"},{"internalType":"address","name":"_tokenIn","type":"address"},{"internalType":"address","name":"_tokenOut","type":"address"},{"internalType":"address[]","name":"_trustedTokens","type":"address[]"},{"internalType":"uint256","name":"_maxSteps","type":"uint256"},{"internalType":"uint256","name":"_gasPrice","type":"uint256"}],"name":"findBestPathWithGas","outputs":[{"components":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"address[]","name":"adapters","type":"address[]"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address[]","name":"recipients","type":"address[]"},{"internalType":"uint256","name":"gasEstimate","type":"uint256"}],"internalType":"struct FormattedOffer","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amountIn","type":"uint256"},{"internalType":"address","name":"_tokenIn","type":"address"},{"internalType":"address","name":"_tokenOut","type":"address"},{"internalType":"uint8","name":"_index","type":"uint8"}],"name":"queryAdapter","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amountIn","type":"uint256"},{"internalType":"address","name":"_tokenIn","type":"address"},{"internalType":"address","name":"_tokenOut","type":"address"},{"internalType":"uint8[]","name":"_options","type":"uint8[]"}],"name":"queryNoSplit","outputs":[{"components":[{"internalType":"address","name":"adapter","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"amountOut","type":"uint256"}],"internalType":"struct Query","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amountIn","type":"uint256"},{"internalType":"address","name":"_tokenIn","type":"address"},{"internalType":"address","name":"_tokenOut","type":"address"}],"name":"queryNoSplit","outputs":[{"components":[{"internalType":"address","name":"adapter","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"amountOut","type":"uint256"}],"internalType":"struct Query","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenAddress","type":"address"},{"internalType":"uint256","name":"_tokenAmount","type":"uint256"}],"name":"recoverERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"recoverNative","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"removedMaintainer","type":"address"}],"name":"removeMaintainer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_adapters","type":"address[]"}],"name":"setAdapters","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_wnative","type":"address"}],"name":"setAllowanceForWrapping","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_claimer","type":"address"}],"name":"setFeeClaimer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_fee","type":"uint256"}],"name":"setMinFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_trustedTokens","type":"address[]"}],"name":"setTrustedTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address[]","name":"adapters","type":"address[]"},{"internalType":"address[]","name":"recipients","type":"address[]"}],"internalType":"struct Trade","name":"_trade","type":"tuple"},{"internalType":"uint256","name":"_fee","type":"uint256"},{"internalType":"address","name":"_to","type":"address"}],"name":"swapNoSplit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address[]","name":"adapters","type":"address[]"},{"internalType":"address[]","name":"recipients","type":"address[]"}],"internalType":"struct Trade","name":"_trade","type":"tuple"},{"internalType":"uint256","name":"_fee","type":"uint256"},{"internalType":"address","name":"_to","type":"address"}],"name":"swapNoSplitFromETH","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address[]","name":"adapters","type":"address[]"},{"internalType":"address[]","name":"recipients","type":"address[]"}],"internalType":"struct Trade","name":"_trade","type":"tuple"},{"internalType":"uint256","name":"_fee","type":"uint256"},{"internalType":"address","name":"_to","type":"address"}],"name":"swapNoSplitToETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address[]","name":"adapters","type":"address[]"},{"internalType":"address[]","name":"recipients","type":"address[]"}],"internalType":"struct Trade","name":"_trade","type":"tuple"},{"internalType":"uint256","name":"_fee","type":"uint256"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_deadline","type":"uint256"},{"internalType":"uint8","name":"_v","type":"uint8"},{"internalType":"bytes32","name":"_r","type":"bytes32"},{"internalType":"bytes32","name":"_s","type":"bytes32"}],"name":"swapNoSplitToETHWithPermit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address[]","name":"adapters","type":"address[]"},{"internalType":"address[]","name":"recipients","type":"address[]"}],"internalType":"struct Trade","name":"_trade","type":"tuple"},{"internalType":"uint256","name":"_fee","type":"uint256"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_deadline","type":"uint256"},{"internalType":"uint8","name":"_v","type":"uint8"},{"internalType":"bytes32","name":"_r","type":"bytes32"},{"internalType":"bytes32","name":"_s","type":"bytes32"}],"name":"swapNoSplitWithPermit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"trustedTokensCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]