文件 1 的 14: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 的 14:BytesManipulation.sol
pragma solidity ^0.8.0;
import "./BytesToTypes.sol";
library BytesManipulation {
function toBytes(uint256 x) internal pure returns (bytes memory b) {
b = new bytes(32);
assembly {
mstore(add(b, 32), x)
}
}
function toBytes(address x) internal pure returns (bytes memory b) {
b = new bytes(32);
assembly {
mstore(add(b, 32), x)
}
}
function mergeBytes(bytes memory a, bytes memory b) public pure returns (bytes memory c) {
uint256 alen = a.length;
uint256 totallen = alen + b.length;
uint256 loopsa = (a.length + 31) / 32;
uint256 loopsb = (b.length + 31) / 32;
assembly {
let m := mload(0x40)
mstore(m, totallen)
for {
let i := 0
} lt(i, loopsa) {
i := add(1, i)
} {
mstore(add(m, mul(32, add(1, i))), mload(add(a, mul(32, add(1, i)))))
}
for {
let i := 0
} lt(i, loopsb) {
i := add(1, i)
} {
mstore(add(m, add(mul(32, add(1, i)), alen)), mload(add(b, mul(32, add(1, i)))))
}
mstore(0x40, add(m, add(32, totallen)))
c := m
}
}
function bytesToAddress(uint256 _offst, bytes memory _input) internal pure returns (address) {
return BytesToTypes.bytesToAddress(_offst, _input);
}
function bytesToUint256(uint256 _offst, bytes memory _input) internal pure returns (uint256) {
return BytesToTypes.bytesToUint256(_offst, _input);
}
}
文件 3 的 14:BytesToTypes.sol
pragma solidity ^0.8.0;
library BytesToTypes {
function bytesToAddress(uint256 _offst, bytes memory _input) internal pure returns (address _output) {
assembly {
_output := mload(add(_input, _offst))
}
}
function bytesToUint256(uint256 _offst, bytes memory _input) internal pure returns (uint256 _output) {
assembly {
_output := mload(add(_input, _offst))
}
}
}
文件 4 的 14: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;
}
}
文件 5 的 14: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;
}
}
文件 6 的 14: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;
}
文件 7 的 14: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);
}
文件 8 的 14:IERC165.sol
pragma solidity ^0.8.0;
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
文件 9 的 14: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);
}
文件 10 的 14:IWETH.sol
pragma solidity ^0.8.0;
import "./IERC20.sol";
interface IWETH is IERC20 {
function withdraw(uint256 amount) external;
function deposit() external payable;
}
文件 11 的 14: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 的 14: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");
}
}
}
文件 13 的 14: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);
}
}
文件 14 的 14:YakRouter.sol
pragma solidity ^0.8.0;
pragma experimental ABIEncoderV2;
import "./lib/BytesManipulation.sol";
import "./interface/IAdapter.sol";
import "./interface/IERC20.sol";
import "./interface/IWETH.sol";
import "./lib/SafeERC20.sol";
import "./lib/Maintainable.sol";
contract YakRouter is Maintainable {
using SafeERC20 for IERC20;
address public immutable WNATIVE;
address public constant NATIVE = address(0);
string public constant NAME = "YakRouter";
uint256 public constant FEE_DENOMINATOR = 1e4;
uint256 public MIN_FEE = 0;
address public FEE_CLAIMER;
address[] public TRUSTED_TOKENS;
address[] public ADAPTERS;
event Recovered(address indexed _asset, uint256 amount);
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);
struct Query {
address adapter;
address tokenIn;
address tokenOut;
uint256 amountOut;
}
struct Offer {
bytes amounts;
bytes adapters;
bytes path;
uint256 gasEstimate;
}
struct FormattedOffer {
uint256[] amounts;
address[] adapters;
address[] path;
uint256 gasEstimate;
}
struct Trade {
uint256 amountIn;
uint256 amountOut;
address[] path;
address[] 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) public onlyMaintainer {
emit UpdatedTrustedTokens(_trustedTokens);
TRUSTED_TOKENS = _trustedTokens;
}
function setAdapters(address[] memory _adapters) public onlyMaintainer {
emit UpdatedAdapters(_adapters);
ADAPTERS = _adapters;
}
function setMinFee(uint256 _fee) external onlyMaintainer {
emit UpdatedMinFee(MIN_FEE, _fee);
MIN_FEE = _fee;
}
function setFeeClaimer(address _claimer) public onlyMaintainer {
emit UpdatedFeeClaimer(FEE_CLAIMER, _claimer);
FEE_CLAIMER = _claimer;
}
function trustedTokensCount() external view returns (uint256) {
return TRUSTED_TOKENS.length;
}
function adaptersCount() external view returns (uint256) {
return ADAPTERS.length;
}
function recoverERC20(address _tokenAddress, uint256 _tokenAmount) external onlyMaintainer {
require(_tokenAmount > 0, "YakRouter: Nothing to recover");
IERC20(_tokenAddress).safeTransfer(msg.sender, _tokenAmount);
emit Recovered(_tokenAddress, _tokenAmount);
}
function recoverAVAX(uint256 _amount) external onlyMaintainer {
require(_amount > 0, "YakRouter: Nothing to recover");
payable(msg.sender).transfer(_amount);
emit Recovered(address(0), _amount);
}
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 _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 _cloneOffer(Offer memory _queries) internal pure returns (Offer memory) {
return Offer(_queries.amounts, _queries.adapters, _queries.path, _queries.gasEstimate);
}
function _addQuery(
Offer memory _queries,
uint256 _amount,
address _adapter,
address _tokenOut,
uint256 _gasEstimate
) internal pure {
_queries.path = BytesManipulation.mergeBytes(_queries.path, BytesManipulation.toBytes(_tokenOut));
_queries.amounts = BytesManipulation.mergeBytes(_queries.amounts, BytesManipulation.toBytes(_amount));
_queries.adapters = BytesManipulation.mergeBytes(_queries.adapters, BytesManipulation.toBytes(_adapter));
_queries.gasEstimate += _gasEstimate;
}
function _formatAmounts(bytes memory _amounts) internal pure returns (uint256[] memory) {
uint256 chunks = _amounts.length / 32;
uint256[] memory amountsFormatted = new uint256[](chunks);
for (uint256 i = 0; i < chunks; i++) {
amountsFormatted[i] = BytesManipulation.bytesToUint256(i * 32 + 32, _amounts);
}
return amountsFormatted;
}
function _formatAddresses(bytes memory _addresses) internal pure returns (address[] memory) {
uint256 chunks = _addresses.length / 32;
address[] memory addressesFormatted = new address[](chunks);
for (uint256 i = 0; i < chunks; i++) {
addressesFormatted[i] = BytesManipulation.bytesToAddress(i * 32 + 32, _addresses);
}
return addressesFormatted;
}
function _formatOffer(Offer memory _queries) internal pure returns (FormattedOffer memory) {
return
FormattedOffer(
_formatAmounts(_queries.amounts),
_formatAddresses(_queries.adapters),
_formatAddresses(_queries.path),
_queries.gasEstimate
);
}
function queryAdapter(
uint256 _amountIn,
address _tokenIn,
address _tokenOut,
uint8 _index
) external view returns (uint256) {
IAdapter _adapter = IAdapter(ADAPTERS[_index]);
uint256 amountOut = _adapter.query(_amountIn, _tokenIn, _tokenOut);
return amountOut;
}
function queryNoSplit(
uint256 _amountIn,
address _tokenIn,
address _tokenOut,
uint8[] calldata _options
) public view returns (Query memory) {
Query memory bestQuery;
for (uint8 i; i < _options.length; i++) {
address _adapter = ADAPTERS[_options[i]];
uint256 amountOut = IAdapter(_adapter).query(_amountIn, _tokenIn, _tokenOut);
if (i == 0 || amountOut > bestQuery.amountOut) {
bestQuery = Query(_adapter, _tokenIn, _tokenOut, amountOut);
}
}
return bestQuery;
}
function queryNoSplit(
uint256 _amountIn,
address _tokenIn,
address _tokenOut
) public view returns (Query memory) {
Query memory bestQuery;
for (uint8 i; i < ADAPTERS.length; i++) {
address _adapter = ADAPTERS[i];
uint256 amountOut = IAdapter(_adapter).query(_amountIn, _tokenIn, _tokenOut);
if (i == 0 || amountOut > bestQuery.amountOut) {
bestQuery = Query(_adapter, _tokenIn, _tokenOut, amountOut);
}
}
return bestQuery;
}
function findBestPathWithGas(
uint256 _amountIn,
address _tokenIn,
address _tokenOut,
uint256 _maxSteps,
uint256 _gasPrice
) external view returns (FormattedOffer memory) {
require(_maxSteps > 0 && _maxSteps < 5, "YakRouter: Invalid max-steps");
Offer memory queries;
queries.amounts = BytesManipulation.toBytes(_amountIn);
queries.path = BytesManipulation.toBytes(_tokenIn);
uint256 gasPriceInExitTkn = _gasPrice > 0 ? getGasPriceInExitTkn(_gasPrice, _tokenOut) : 0;
queries = _findBestPath(_amountIn, _tokenIn, _tokenOut, _maxSteps, queries, gasPriceInExitTkn);
if (queries.adapters.length == 0) {
queries.amounts = "";
queries.path = "";
}
return _formatOffer(queries);
}
function getGasPriceInExitTkn(uint256 _gasPrice, address _tokenOut) internal view returns (uint256 price) {
FormattedOffer memory gasQuery = findBestPath(1e18, WNATIVE, _tokenOut, 2);
if (gasQuery.path.length != 0) {
price = (gasQuery.amounts[gasQuery.amounts.length - 1] * _gasPrice) / 1e9;
}
}
function findBestPath(
uint256 _amountIn,
address _tokenIn,
address _tokenOut,
uint256 _maxSteps
) public view returns (FormattedOffer memory) {
require(_maxSteps > 0 && _maxSteps < 5, "YakRouter: Invalid max-steps");
Offer memory queries;
queries.amounts = BytesManipulation.toBytes(_amountIn);
queries.path = BytesManipulation.toBytes(_tokenIn);
queries = _findBestPath(_amountIn, _tokenIn, _tokenOut, _maxSteps, queries, 0);
if (queries.adapters.length == 0) {
queries.amounts = "";
queries.path = "";
}
return _formatOffer(queries);
}
function _findBestPath(
uint256 _amountIn,
address _tokenIn,
address _tokenOut,
uint256 _maxSteps,
Offer memory _queries,
uint256 _tknOutPriceNwei
) internal view returns (Offer memory) {
Offer memory bestOption = _cloneOffer(_queries);
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();
}
_addQuery(bestOption, queryDirect.amountOut, queryDirect.adapter, queryDirect.tokenOut, gasEstimate);
bestAmountOut = queryDirect.amountOut;
}
if (_maxSteps > 1 && _queries.adapters.length / 32 <= _maxSteps - 2) {
for (uint256 i = 0; i < TRUSTED_TOKENS.length; i++) {
if (_tokenIn == TRUSTED_TOKENS[i]) {
continue;
}
Query memory bestSwap = queryNoSplit(_amountIn, _tokenIn, TRUSTED_TOKENS[i]);
if (bestSwap.amountOut == 0) {
continue;
}
Offer memory newOffer = _cloneOffer(_queries);
if (withGas) {
gasEstimate = IAdapter(bestSwap.adapter).swapGasEstimate();
}
_addQuery(newOffer, bestSwap.amountOut, bestSwap.adapter, bestSwap.tokenOut, gasEstimate);
newOffer = _findBestPath(
bestSwap.amountOut,
TRUSTED_TOKENS[i],
_tokenOut,
_maxSteps,
newOffer,
_tknOutPriceNwei
);
address tokenOut = BytesManipulation.bytesToAddress(newOffer.path.length, newOffer.path);
uint256 amountOut = BytesManipulation.bytesToUint256(newOffer.amounts.length, newOffer.amounts);
if (_tokenOut == tokenOut && amountOut > bestAmountOut) {
if (newOffer.gasEstimate > bestOption.gasEstimate) {
uint256 gasCostDiff = (_tknOutPriceNwei * (newOffer.gasEstimate - bestOption.gasEstimate)) /
1e9;
uint256 priceDiff = amountOut - bestAmountOut;
if (gasCostDiff > priceDiff) {
continue;
}
}
bestAmountOut = amountOut;
bestOption = newOffer;
}
}
}
return bestOption;
}
function _swapNoSplit(
Trade calldata _trade,
address _from,
address _to,
uint256 _fee
) internal returns (uint256) {
uint256[] memory amounts = new uint256[](_trade.path.length);
if (_fee > 0 || MIN_FEE > 0) {
amounts[0] = _applyFee(_trade.amountIn, _fee);
_transferFrom(_trade.path[0], _from, FEE_CLAIMER, _trade.amountIn - amounts[0]);
} else {
amounts[0] = _trade.amountIn;
}
_transferFrom(_trade.path[0], _from, _trade.adapters[0], amounts[0]);
for (uint256 i = 0; i < _trade.adapters.length; i++) {
amounts[i + 1] = IAdapter(_trade.adapters[i]).query(amounts[i], _trade.path[i], _trade.path[i + 1]);
}
require(amounts[amounts.length - 1] >= _trade.amountOut, "YakRouter: Insufficient output amount");
for (uint256 i = 0; i < _trade.adapters.length; i++) {
address targetAddress = i < _trade.adapters.length - 1 ? _trade.adapters[i + 1] : _to;
IAdapter(_trade.adapters[i]).swap(
amounts[i],
amounts[i + 1],
_trade.path[i],
_trade.path[i + 1],
targetAddress
);
}
emit YakSwap(_trade.path[0], _trade.path[_trade.path.length - 1], _trade.amountIn, amounts[amounts.length - 1]);
return amounts[amounts.length - 1];
}
function swapNoSplit(
Trade calldata _trade,
address _to,
uint256 _fee
) public {
_swapNoSplit(_trade, msg.sender, _to, _fee);
}
function swapNoSplitFromAVAX(
Trade calldata _trade,
address _to,
uint256 _fee
) external payable {
require(_trade.path[0] == WNATIVE, "YakRouter: Path needs to begin with WAVAX");
_wrap(_trade.amountIn);
_swapNoSplit(_trade, address(this), _to, _fee);
}
function swapNoSplitToAVAX(
Trade calldata _trade,
address _to,
uint256 _fee
) public {
require(_trade.path[_trade.path.length - 1] == WNATIVE, "YakRouter: Path needs to end with WAVAX");
uint256 returnAmount = _swapNoSplit(_trade, msg.sender, address(this), _fee);
_unwrap(returnAmount);
_returnTokensTo(NATIVE, returnAmount, _to);
}
function swapNoSplitWithPermit(
Trade calldata _trade,
address _to,
uint256 _fee,
uint256 _deadline,
uint8 _v,
bytes32 _r,
bytes32 _s
) external {
IERC20(_trade.path[0]).permit(msg.sender, address(this), _trade.amountIn, _deadline, _v, _r, _s);
swapNoSplit(_trade, _to, _fee);
}
function swapNoSplitToAVAXWithPermit(
Trade calldata _trade,
address _to,
uint256 _fee,
uint256 _deadline,
uint8 _v,
bytes32 _r,
bytes32 _s
) external {
IERC20(_trade.path[0]).permit(msg.sender, address(this), _trade.amountIn, _deadline, _v, _r, _s);
swapNoSplitToAVAX(_trade, _to, _fee);
}
}
{
"compilationTarget": {
"src/contracts/YakRouter.sol": "YakRouter"
},
"evmVersion": "istanbul",
"libraries": {
"src/contracts/lib/BytesManipulation.sol:BytesManipulation": "0xa479b3af0476a9e605133975c1f0c414d0654d12"
},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 999
},
"remappings": []
}
[{"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":"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":"uint256","name":"gasEstimate","type":"uint256"}],"internalType":"struct YakRouter.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":"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":"uint256","name":"gasEstimate","type":"uint256"}],"internalType":"struct YakRouter.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"}],"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":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"amountOut","type":"uint256"}],"internalType":"struct YakRouter.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":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"amountOut","type":"uint256"}],"internalType":"struct YakRouter.Query","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"recoverAVAX","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenAddress","type":"address"},{"internalType":"uint256","name":"_tokenAmount","type":"uint256"}],"name":"recoverERC20","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":"struct YakRouter.Trade","name":"_trade","type":"tuple"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_fee","type":"uint256"}],"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":"struct YakRouter.Trade","name":"_trade","type":"tuple"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_fee","type":"uint256"}],"name":"swapNoSplitFromAVAX","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":"struct YakRouter.Trade","name":"_trade","type":"tuple"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_fee","type":"uint256"}],"name":"swapNoSplitToAVAX","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":"struct YakRouter.Trade","name":"_trade","type":"tuple"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_fee","type":"uint256"},{"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":"swapNoSplitToAVAXWithPermit","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":"struct YakRouter.Trade","name":"_trade","type":"tuple"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_fee","type":"uint256"},{"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"}]