// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)pragmasolidity ^0.8.0;/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/abstractcontractContext{
function_msgSender() internalviewvirtualreturns (address) {
returnmsg.sender;
}
function_msgData() internalviewvirtualreturns (bytescalldata) {
returnmsg.data;
}
}
Contract Source Code
File 3 of 18: IERC20.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)pragmasolidity ^0.8.0;/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/interfaceIERC20{
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/eventTransfer(addressindexedfrom, addressindexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/eventApproval(addressindexed owner, addressindexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/functiontotalSupply() externalviewreturns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/functionbalanceOf(address account) externalviewreturns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/functiontransfer(address to, uint256 amount) externalreturns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/functionallowance(address owner, address spender) externalviewreturns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/functionapprove(address spender, uint256 amount) externalreturns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/functiontransferFrom(addressfrom, address to, uint256 amount) externalreturns (bool);
}
// SPDX-License-Identifier: GPL-2.0-or-laterpragmasolidity >=0.7.5;pragmaabicoderv2;interfaceISwapRouter{
structExactInputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
uint160 sqrtPriceLimitX96;
}
/// @notice Swaps `amountIn` of one token for as much as possible of another token/// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata/// @return amountOut The amount of the received tokenfunctionexactInputSingle(
ExactInputSingleParams calldata params
) externalpayablereturns (uint256 amountOut);
structExactInputParams {
bytes path;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
}
/// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path/// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata/// @return amountOut The amount of the received tokenfunctionexactInput(
ExactInputParams calldata params
) externalpayablereturns (uint256 amountOut);
}
Contract Source Code
File 6 of 18: IUniswapV3Factory.sol
// SPDX-License-Identifier: GPL-2.0-or-laterpragmasolidity >=0.5.0;interfaceIUniswapV3Factory{
/// @notice Returns the pool address for a given pair of tokens and a fee, or address 0 if it does not exist/// @dev tokenA and tokenB may be passed in either token0/token1 or token1/token0 order/// @param tokenA The contract address of either token0 or token1/// @param tokenB The contract address of the other token/// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip/// @return pool The pool addressfunctiongetPool(address tokenA,
address tokenB,
uint24 fee
) externalviewreturns (address pool);
}
Contract Source Code
File 7 of 18: IUniswapV3Pool.sol
// SPDX-License-Identifier: GPL-2.0-or-laterpragmasolidity >=0.5.0;interfaceIUniswapV3Pool{
/// @notice The 0th storage slot in the pool stores many values, and is exposed as a single method to save gas/// when accessed externally./// @return sqrtPriceX96 The current price of the pool as a sqrt(token1/token0) Q64.96 value/// tick The current tick of the pool, i.e. according to the last tick transition that was run./// This value may not always be equal to SqrtTickMath.getTickAtSqrtRatio(sqrtPriceX96) if the price is on a tick/// boundary./// observationIndex The index of the last oracle observation that was written,/// observationCardinality The current maximum number of observations stored in the pool,/// observationCardinalityNext The next maximum number of observations, to be updated when the observation./// feeProtocol The protocol fee for both tokens of the pool./// Encoded as two 4 bit values, where the protocol fee of token1 is shifted 4 bits and the protocol fee of token0/// is the lower 4 bits. Used as the denominator of a fraction of the swap fee, e.g. 4 means 1/4th of the swap fee./// unlocked Whether the pool is currently locked to reentrancyfunctionslot0()
externalviewreturns (uint160 sqrtPriceX96,
int24 tick,
uint16 observationIndex,
uint16 observationCardinality,
uint16 observationCardinalityNext,
uint8 feeProtocol,
bool unlocked
);
/// @notice The first of the two tokens of the pool, sorted by address/// @return The token contract addressfunctiontoken0() externalviewreturns (address);
/// @notice The second of the two tokens of the pool, sorted by address/// @return The token contract addressfunctiontoken1() externalviewreturns (address);
/// @notice The pool's fee in hundredths of a bip, i.e. 1e-6/// @return The feefunctionfee() externalviewreturns (uint24);
/// @notice The pool tick spacing/// @dev Ticks can only be used at multiples of this value, minimum of 1 and always positive/// e.g.: a tickSpacing of 3 means ticks can be initialized every 3rd tick, i.e., ..., -6, -3, 0, 3, 6, .../// This value is an int24 to avoid casting even though it is always positive./// @return The tick spacingfunctiontickSpacing() externalviewreturns (int24);
}
Contract Source Code
File 8 of 18: IWETH9.sol
// SPDX-License-Identifier: GPL-2.0-or-laterpragmasolidity ^0.8.0;/// @title Interface for WETH9interfaceIWETH9{
/// @notice Deposit ether to get wrapped etherfunctiondeposit() externalpayable;
/// @notice Withdraw wrapped ether to get etherfunctionwithdraw(uint256 amount) external;
}
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)pragmasolidity ^0.8.0;import"../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/abstractcontractOwnableisContext{
addressprivate _owner;
eventOwnershipTransferred(addressindexed previousOwner, addressindexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/constructor() {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/modifieronlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/functionowner() publicviewvirtualreturns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/function_checkOwner() internalviewvirtual{
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/functionrenounceOwnership() publicvirtualonlyOwner{
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/functiontransferOwnership(address newOwner) publicvirtualonlyOwner{
require(newOwner !=address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/function_transferOwnership(address newOwner) internalvirtual{
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/SafeMath.sol)pragmasolidity ^0.8.0;// CAUTION// This version of SafeMath should only be used with Solidity 0.8 or later,// because it relies on the compiler's built in overflow checks./**
* @dev Wrappers over Solidity's arithmetic operations.
*
* NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler
* now has built in overflow checking.
*/librarySafeMath{
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/functiontryAdd(uint256 a, uint256 b) internalpurereturns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/functiontrySub(uint256 a, uint256 b) internalpurereturns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/functiontryMul(uint256 a, uint256 b) internalpurereturns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the// benefit is lost if 'b' is also tested.// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522if (a ==0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/functiontryDiv(uint256 a, uint256 b) internalpurereturns (bool, uint256) {
unchecked {
if (b ==0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/functiontryMod(uint256 a, uint256 b) internalpurereturns (bool, uint256) {
unchecked {
if (b ==0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
*
* - Addition cannot overflow.
*/functionadd(uint256 a, uint256 b) internalpurereturns (uint256) {
return a + b;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/functionsub(uint256 a, uint256 b) internalpurereturns (uint256) {
return a - b;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
*
* - Multiplication cannot overflow.
*/functionmul(uint256 a, uint256 b) internalpurereturns (uint256) {
return a * b;
}
/**
* @dev Returns the integer division of two unsigned integers, reverting on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator.
*
* Requirements:
*
* - The divisor cannot be zero.
*/functiondiv(uint256 a, uint256 b) internalpurereturns (uint256) {
return a / b;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/functionmod(uint256 a, uint256 b) internalpurereturns (uint256) {
return a % b;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {trySub}.
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/functionsub(uint256 a, uint256 b, stringmemory errorMessage) internalpurereturns (uint256) {
unchecked {
require(b <= a, errorMessage);
return a - b;
}
}
/**
* @dev Returns the integer division of two unsigned integers, reverting with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/functiondiv(uint256 a, uint256 b, stringmemory errorMessage) internalpurereturns (uint256) {
unchecked {
require(b >0, errorMessage);
return a / b;
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting with custom message when dividing by zero.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryMod}.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/functionmod(uint256 a, uint256 b, stringmemory errorMessage) internalpurereturns (uint256) {
unchecked {
require(b >0, errorMessage);
return a % b;
}
}
}
Contract Source Code
File 15 of 18: TransferHelper.sol
// SPDX-License-Identifier: GPL-2.0-or-laterpragmasolidity >=0.6.0;import"@openzeppelin/contracts/token/ERC20/IERC20.sol";
libraryTransferHelper{
/// @notice Transfers tokens from the targeted address to the given destination/// @notice Errors with 'STF' if transfer fails/// @param token The contract address of the token to be transferred/// @param from The originating address from which the tokens will be transferred/// @param to The destination address of the transfer/// @param value The amount to be transferredfunctionsafeTransferFrom(address token,
addressfrom,
address to,
uint256 value
) internal{
(bool success, bytesmemory data) = token.call(
abi.encodeWithSelector(
IERC20.transferFrom.selector,
from,
to,
value
)
);
require(
success && (data.length==0||abi.decode(data, (bool))),
"STF"
);
}
/// @notice Transfers tokens from msg.sender to a recipient/// @dev Errors with ST if transfer fails/// @param token The contract address of the token which will be transferred/// @param to The recipient of the transfer/// @param value The value of the transferfunctionsafeTransfer(address token, address to, uint256 value) internal{
(bool success, bytesmemory data) = token.call(
abi.encodeWithSelector(IERC20.transfer.selector, to, value)
);
require(
success && (data.length==0||abi.decode(data, (bool))),
"ST"
);
}
/// @notice Approves the stipulated contract to spend the given allowance in the given token/// @dev Errors with 'SA' if transfer fails/// @param token The contract address of the token to be approved/// @param to The target of the approval/// @param value The amount of the given token the target will be allowed to spendfunctionsafeApprove(address token, address to, uint256 value) internal{
(bool success, bytesmemory data) = token.call(
abi.encodeWithSelector(IERC20.approve.selector, to, value)
);
require(
success && (data.length==0||abi.decode(data, (bool))),
"SA"
);
}
/// @notice Transfers ETH to the recipient address/// @dev Fails with `STE`/// @param to The destination of the transfer/// @param value The value to be transferredfunctionsafeTransferETH(address to, uint256 value) internal{
(bool success, ) = to.call{value: value}(newbytes(0));
require(success, "STE");
}
}
Contract Source Code
File 16 of 18: Zap.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.0;import"./interfaces/external/IERC20Querier.sol";
import"./interfaces/external/IWETH9.sol";
import"./interfaces/IZap.sol";
import"./interfaces/IZapEvent.sol";
import"./interfaces/uniswapV3/ISwapRouter.sol";
import"./libraries/constants/ZapConstants.sol";
import"./libraries/constants/Constants.sol";
import"./libraries/uniswapV3/TransferHelper.sol";
import"./libraries/ParameterVerificationHelper.sol";
import"./libraries/PoolHelper.sol";
import"./ZapInitializer.sol";
import"@openzeppelin/contracts/access/Ownable.sol";
import"@openzeppelin/contracts/utils/math/SafeMath.sol";
/// @dev verified, public contract/// @dev ceratin functions only owner callablecontractZapisIZap, IZapEvent, Ownable, ZapInitializer{
usingSafeMathforuint256;
uint24publicoverride slippageToleranceNumerator;
addresspublic WETH;
constructor(uint24 _slippageToleranceNumerator) {
// initialize pre-defined swapPath and swapTradeFeeNumerator
initializeSwapTradeFeeNumerator();
initializeSwapPath();
slippageToleranceNumerator = _slippageToleranceNumerator;
WETH = Constants.WETH_ADDRESS;
}
functiongetSwapInfo(address inputToken,
address outputToken
)
publicviewoverridereturns (bool isPathDefined,
address[] memory swapPathArray,
uint24[] memory swapTradeFeeArray
)
{
// parameter verification
ParameterVerificationHelper.verifyNotZeroAddress(inputToken);
ParameterVerificationHelper.verifyNotZeroAddress(outputToken);
// verify inputToken is not outputTokenrequire(inputToken != outputToken, "inputToken == outputToken");
// get swapPathaddress[] memory _swapPathArray = swapPath[inputToken][outputToken];
uint256 pathLength = _swapPathArray.length;
if (pathLength >=2) {
// statement for "single swap path" & "multiple swap path"bool _isPathDefined =true;
uint24[] memory _swapTradeFeeArray =newuint24[](pathLength -1);
for (uint i =0; i < (pathLength -1); i++) {
uint24 tradeFee = swapTradeFeeNumerator[_swapPathArray[i]][
_swapPathArray[i +1]
];
if (tradeFee ==0) {
// path not defined if tradeFee is 0
_isPathDefined =false;
}
_swapTradeFeeArray[i] = tradeFee;
}
return (_isPathDefined, _swapPathArray, _swapTradeFeeArray);
} else {
// statement for "path is not defined"return (false, newaddress[](0), newuint24[](0));
}
}
functionsetSlippageToleranceNumerator(uint24 slippageTolerance
) publiconlyOwner{
// parameter verification
ParameterVerificationHelper.verifyGreaterThanZero(slippageTolerance);
// verify slippageTolerance is less than SLIPPAGE_TOLERANCE_DENOMINATORrequire(
slippageTolerance < ZapConstants.SLIPPAGE_TOLERANCE_DENOMINATOR,
"slippageTolerance too big"
);
// update slippageToleranceNumerator
slippageToleranceNumerator = slippageTolerance;
// emit UpdateSlippageTolerance eventemit UpdateSlippageTolerance(slippageTolerance);
}
functionsetSwapTradeFeeNumerator(address inputToken,
address outputToken,
uint24 swapTradeFee
) publiconlyOwner{
// parameter verification
ParameterVerificationHelper.verifyNotZeroAddress(inputToken);
ParameterVerificationHelper.verifyNotZeroAddress(outputToken);
ParameterVerificationHelper.verifyGreaterThanZero(swapTradeFee);
// verify inputToken is not outputTokenrequire(inputToken != outputToken, "inputToken == outputToken");
// verify pool is existaddress poolAddress = PoolHelper.getPoolAddress(
Constants.UNISWAP_V3_FACTORY_ADDRESS,
inputToken,
outputToken,
swapTradeFee
);
require(poolAddress !=address(0), "pool not exist");
// update swapTradeFeeNumerator
swapTradeFeeNumerator[inputToken][outputToken] = swapTradeFee;
// emit UpdateSwapTradeFee eventemit UpdateSwapTradeFee(inputToken, outputToken, swapTradeFee);
}
functionsetSwapPath(address inputToken,
address outputToken,
address[] memory newSwapPath
) publiconlyOwner{
// parameter verification
ParameterVerificationHelper.verifyNotZeroAddress(inputToken);
ParameterVerificationHelper.verifyNotZeroAddress(outputToken);
uint256 pathLength = newSwapPath.length;
for (uint i =0; i < pathLength; i++) {
ParameterVerificationHelper.verifyNotZeroAddress(newSwapPath[i]);
}
// verify inputToken is not outputTokenrequire(inputToken != outputToken, "inputToken == outputToken");
// verify input path is valid swap pathrequire(pathLength >=2, "path too short");
// verify first token in newSwapPath is inputTokenrequire(newSwapPath[0] == inputToken, "path not start from inputToken");
// verify last token in newSwapPath is outputTokenrequire(
newSwapPath[(pathLength -1)] == outputToken,
"path not end with outputToken"
);
// verify each swap’s fee is definedfor (uint i =0; i < (pathLength -1); i++) {
uint24 tradeFee = swapTradeFeeNumerator[newSwapPath[i]][
newSwapPath[i +1]
];
require(tradeFee !=0, "tradefee not defined");
}
// update Swap Path
swapPath[inputToken][outputToken] = newSwapPath;
// emit UpdateSwapPath eventemit UpdateSwapPath(inputToken, outputToken, newSwapPath);
}
functiongetTokenExchangeRate(address inputToken,
address outputToken
)
publicviewoverridereturns (address token0,
address token1,
uint256 tokenPriceWith18Decimals
)
{
// parameter verification
ParameterVerificationHelper.verifyNotZeroAddress(inputToken);
ParameterVerificationHelper.verifyNotZeroAddress(outputToken);
// verify inputToken is not outputTokenrequire(inputToken != outputToken, "inputToken == outputToken");
// verify swap trade fee is defineduint24 tradeFee = swapTradeFeeNumerator[inputToken][outputToken];
require(tradeFee !=0, "tradeFee not define");
// verify pool is existaddress poolAddress = PoolHelper.getPoolAddress(
Constants.UNISWAP_V3_FACTORY_ADDRESS,
inputToken,
outputToken,
tradeFee
);
require(poolAddress !=address(0), "pool not exist");
// query pool info
(token0, token1, , , , , ) = PoolHelper.getPoolInfo(poolAddress);
// calculate token price with 18 decimal precision
tokenPriceWith18Decimals = PoolHelper.getTokenPriceWithDecimalsByPool(
poolAddress,
ZapConstants.DECIMALS_PRECISION
);
}
functiongetMinimumSwapOutAmount(address inputToken,
address outputToken,
uint256 inputAmount
) publicviewoverridereturns (uint256 minimumSwapOutAmount) {
uint256 estimateSwapOutAmount = getEstimateSwapOutAmount(
inputToken,
outputToken,
inputAmount
);
// calculate price include slippage toleranceuint256 _minimumSwapOutAmount = estimateSwapOutAmount
.mul(
uint256(ZapConstants.SLIPPAGE_TOLERANCE_DENOMINATOR).sub(
slippageToleranceNumerator
)
)
.div(ZapConstants.SLIPPAGE_TOLERANCE_DENOMINATOR);
minimumSwapOutAmount = _minimumSwapOutAmount;
}
functiongetEstimateSwapOutAmount(address inputToken,
address outputToken,
uint256 inputAmount
) publicviewreturns (uint256 estimateSwapOutAmount) {
// parameter verification
ParameterVerificationHelper.verifyNotZeroAddress(inputToken);
ParameterVerificationHelper.verifyNotZeroAddress(outputToken);
ParameterVerificationHelper.verifyGreaterThanZero(inputAmount);
// variable verificationrequire(
slippageToleranceNumerator >0,
"slippageToleranceNumerator is 0"
);
// verify inputToken is not outputTokenrequire(inputToken != outputToken, "inputToken == outputToken");
// verify swap info is defined
(
bool isPathDefined,
address[] memory swapPathArray,
uint24[] memory swapTradeFeeArray
) = getSwapInfo(inputToken, outputToken);
require(isPathDefined ==true, "path not define");
// get swap path length as loop end indexuint256 pathLength = swapPathArray.length;
// intput token decimal adjustmentuint256 calcAmount = inputAmount.mul(
10** (PoolHelper.getTokenDecimalAdjustment(inputToken))
);
// Loop calculate minimum swap out amountfor (uint i =0; i < (pathLength -1); i++) {
address tokenIn = swapPathArray[i];
address tokenOut = swapPathArray[i +1];
(
address token0,
address token1,
uint256 tokenPriceWith18Decimals // (token1/token0) * 10**DECIMALS_PRECISION
) = getTokenExchangeRate(tokenIn, tokenOut);
// deduct trade fee
calcAmount = calcAmount
.mul(
uint256(ZapConstants.SWAP_TRADE_FEE_DENOMINATOR).sub(
swapTradeFeeArray[i]
)
)
.div(ZapConstants.SWAP_TRADE_FEE_DENOMINATOR);
// get swap out amount without slippagerequire(tokenIn == token0 || tokenIn == token1);
if (tokenIn == token0) {
calcAmount = calcAmount.mul(tokenPriceWith18Decimals).div(
10** ZapConstants.DECIMALS_PRECISION
);
} else {
calcAmount = calcAmount
.mul(10** ZapConstants.DECIMALS_PRECISION)
.div(tokenPriceWith18Decimals);
}
}
// output token decimal adjustment
estimateSwapOutAmount = calcAmount.div(
10** (PoolHelper.getTokenDecimalAdjustment(outputToken))
);
}
/// @notice caller need to approve inputToken to Zap contract in inputAmount amountfunctionswapToken(bool isETH,
address inputToken,
address outputToken,
uint256 inputAmount,
address recipient
) publicpayableoverridereturns (uint256 outputAmount) {
// get minimum swap out amountuint256 minimumSwapOutAmount = getMinimumSwapOutAmount(
inputToken,
outputToken,
inputAmount
);
outputAmount = swapTokenWithMinimumOutput(
isETH,
inputToken,
outputToken,
inputAmount,
minimumSwapOutAmount,
recipient
);
}
/// @notice caller need to approve inputToken to Zap contract in inputAmount amountfunctionswapTokenWithMinimumOutput(bool isETH,
address inputToken,
address outputToken,
uint256 inputAmount,
uint256 minimumSwapOutAmount,
address recipient
) publicpayableoverridereturns (uint256 outputAmount) {
// parameter verification
ParameterVerificationHelper.verifyNotZeroAddress(inputToken);
ParameterVerificationHelper.verifyNotZeroAddress(outputToken);
ParameterVerificationHelper.verifyNotZeroAddress(recipient);
ParameterVerificationHelper.verifyGreaterThanZero(inputAmount);
// verify inputToken is not outputTokenrequire(inputToken != outputToken, "inputToken == outputToken");
// verify swap info is defined
(
bool isPathDefined,
address[] memory swapPathArray,
uint24[] memory swapTradeFeeArray
) = getSwapInfo(inputToken, outputToken);
require(isPathDefined ==true, "path not define");
if (isETH) {
// verify input ETH is the same as inputAmount
ParameterVerificationHelper.verifyMsgValueEqualsInputAmount(
inputAmount
);
require(
inputToken == WETH,
"input ETH must have swap path from WETH"
);
// prepare WETH for swap
IWETH9(WETH).deposit{value: inputAmount}();
} else {
// verify caller inputToken allowance is more or equal to inputAmountrequire(
IERC20Querier(inputToken).allowance(
msg.sender,
address(this)
) >= inputAmount,
"allowance insufficient"
);
// transfer inputToken in inputAmount from caller to Zap contract
TransferHelper.safeTransferFrom(
inputToken,
msg.sender,
address(this),
inputAmount
);
}
// approve inputToken to SmartRouter in inputAmount amount
TransferHelper.safeApprove(
inputToken,
Constants.SWAP_ROUTER_ADDRESS,
inputAmount
);
uint256 pathLength = swapPathArray.length;
if (pathLength ==2) {
// statement for "single swap path", swap by exactInputSingle function
outputAmount = ISwapRouter(Constants.SWAP_ROUTER_ADDRESS)
.exactInputSingle(
ISwapRouter.ExactInputSingleParams(
inputToken,
outputToken,
swapTradeFeeArray[0],
recipient,
block.timestamp.add(transactionDeadlineDuration),
inputAmount,
minimumSwapOutAmount,
0
)
);
// emit SingleSwap eventemit SingleSwap(
recipient,
isETH,
inputToken,
inputAmount,
outputToken,
outputAmount,
swapPathArray,
swapTradeFeeArray
);
} else {
// statement for "multiple swap path", swap by exactInput functionbytesmemory path =abi.encodePacked(swapPathArray[0]);
for (uint i =0; i < (pathLength -1); i++) {
path =abi.encodePacked(
path,
swapTradeFeeArray[i],
swapPathArray[i +1]
);
}
outputAmount = ISwapRouter(Constants.SWAP_ROUTER_ADDRESS)
.exactInput(
ISwapRouter.ExactInputParams(
path,
recipient,
block.timestamp.add(transactionDeadlineDuration),
inputAmount,
minimumSwapOutAmount
)
);
// emit MultiSwap eventemit MultiSwap(
recipient,
isETH,
inputToken,
inputAmount,
outputToken,
outputAmount,
swapPathArray,
swapTradeFeeArray
);
}
}
}