注意:truePriceTokenA,truePriceTokenB是比值价格,不是常识里的价格,如果是2个A换1个B,那么truePriceTokenA为2,truePriceTokenB为1
pragma solidity =0.6.6;
import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol';
import '@uniswap/lib/contracts/libraries/Babylonian.sol';
import '@uniswap/lib/contracts/libraries/TransferHelper.sol';
import '../interfaces/IERC20.sol';
import '../interfaces/IUniswapV2Router01.sol';
import '../libraries/SafeMath.sol';
import '../libraries/UniswapV2Library.sol';
contract ExampleSwapToPrice {
using SafeMath for uint256;
IUniswapV2Router01 public immutable router;
address public immutable factory;
constructor(address factory_, IUniswapV2Router01 router_) public {
factory = factory_;
router = router_;
}
// computes the direction and magnitude of the profit-maximizing trade
function computeProfitMaximizingTrade(
uint256 truePriceTokenA,
uint256 truePriceTokenB,
uint256 reserveA,
uint256 reserveB
) pure public returns (bool aToB, uint256 amountIn) {
aToB = reserveA.mul(truePriceTokenB) / reserveB < truePriceTokenA;
uint256 invariant = reserveA.mul(reserveB);
uint256 leftSide = Babylonian.sqrt(
invariant.mul(aToB ? truePriceTokenA : truePriceTokenB).mul(1000) /
uint256(aToB ? truePriceTokenB : truePriceTokenA).mul(997)
);
uint256 rightSide = (aToB ? reserveA.mul(1000) : reserveB.mul(1000)) / 997;
// compute the amount that must be sent to move the price to the profit-maximizing price
amountIn = leftSide.sub(rightSide);
}
// swaps an amount of either token such that the trade is profit-maximizing, given an external true price
// true price is expressed in the ratio of token A to token B
// caller must approve this contract to spend whichever token is intended to be swapped
function swapToPrice(
address tokenA,
address tokenB,
uint256 truePriceTokenA,
uint256 truePriceTokenB,
uint256 maxSpendTokenA,
uint256 maxSpendTokenB,
address to,
uint256 deadline
) public {
// true price is expressed as a ratio, so both values must be non-zero
require(truePriceTokenA != 0 && truePriceTokenB != 0, "ExampleSwapToPrice: ZERO_PRICE");
// caller can specify 0 for either if they wish to swap in only one direction, but not both
require(maxSpendTokenA != 0 || maxSpendTokenB != 0, "ExampleSwapToPrice: ZERO_SPEND");
bool aToB;
uint256 amountIn;
{
(uint256 reserveA, uint256 reserveB) = UniswapV2Library.getReserves(factory, tokenA, tokenB);
(aToB, amountIn) = computeProfitMaximizingTrade(
truePriceTokenA, truePriceTokenB,
reserveA, reserveB
);
}
// spend up to the allowance of the token in
uint256 maxSpend = aToB ? maxSpendTokenA : maxSpendTokenB;
if (amountIn > maxSpend) {
amountIn = maxSpend;
}
address tokenIn = aToB ? tokenA : tokenB;
address tokenOut = aToB ? tokenB : tokenA;
TransferHelper.safeTransferFrom(tokenIn, msg.sender, address(this), amountIn);
TransferHelper.safeApprove(tokenIn, address(router), amountIn);
address[] memory path = new address[](2);
path[0] = tokenIn;
path[1] = tokenOut;
router.swapExactTokensForTokens(
amountIn,
0, // amountOutMin: we can skip computing this number because the math is tested
path,
to,
deadline
);
}
}