proxied swapToLimit
This commit is contained in:
@@ -6,6 +6,7 @@ import {IERC20} from "../lib/openzeppelin-contracts/contracts/token/ERC20/IERC20
|
||||
import {SafeERC20} from "../lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
|
||||
import {LMSRStabilized} from "./LMSRStabilized.sol";
|
||||
import {PartyPoolBase} from "./PartyPoolBase.sol";
|
||||
import {IPartyPool} from "./IPartyPool.sol";
|
||||
|
||||
/// @title PartyPoolSwapMintImpl - Implementation contract for swapMint and burnSwap functions
|
||||
/// @notice This contract contains the swapMint and burnSwap implementation that will be called via delegatecall
|
||||
@@ -15,5 +16,127 @@ contract PartyPoolSwapImpl is PartyPoolBase {
|
||||
using LMSRStabilized for LMSRStabilized.State;
|
||||
using SafeERC20 for IERC20;
|
||||
|
||||
function swapToLimitAmounts(
|
||||
uint256 inputTokenIndex,
|
||||
uint256 outputTokenIndex,
|
||||
int128 limitPrice,
|
||||
uint256[] memory bases,
|
||||
int128 kappa,
|
||||
int128[] memory qInternal,
|
||||
uint256 swapFeePpm
|
||||
) external pure returns (uint256 amountIn, uint256 amountOut, uint256 fee) {
|
||||
// Compute internal maxima at the price limit
|
||||
(int128 amountInInternal, int128 amountOutInternal) = LMSRStabilized.swapAmountsForPriceLimit(
|
||||
bases.length, kappa, qInternal,
|
||||
inputTokenIndex, outputTokenIndex, limitPrice);
|
||||
|
||||
// Convert input to uint (ceil) and output to uint (floor)
|
||||
uint256 amountInUintNoFee = _internalToUintCeil(amountInInternal, bases[inputTokenIndex]);
|
||||
require(amountInUintNoFee > 0, "swapToLimit: input zero");
|
||||
|
||||
fee = 0;
|
||||
amountIn = amountInUintNoFee;
|
||||
if (swapFeePpm > 0) {
|
||||
fee = _ceilFee(amountInUintNoFee, swapFeePpm);
|
||||
amountIn += fee;
|
||||
}
|
||||
|
||||
amountOut = _internalToUintFloor(amountOutInternal, bases[outputTokenIndex]);
|
||||
require(amountOut > 0, "swapToLimit: output zero");
|
||||
}
|
||||
|
||||
|
||||
function swapToLimit(
|
||||
address payer,
|
||||
address receiver,
|
||||
uint256 inputTokenIndex,
|
||||
uint256 outputTokenIndex,
|
||||
int128 limitPrice,
|
||||
uint256 deadline,
|
||||
uint256 swapFeePpm,
|
||||
uint256 protocolFeePpm
|
||||
) external returns (uint256 amountInUsed, uint256 amountOut, uint256 fee) {
|
||||
uint256 n = tokens.length;
|
||||
require(inputTokenIndex < n && outputTokenIndex < n, "swapToLimit: idx");
|
||||
require(limitPrice > int128(0), "swapToLimit: limit <= 0");
|
||||
require(deadline == 0 || block.timestamp <= deadline, "swapToLimit: deadline exceeded");
|
||||
|
||||
// Read previous balances for affected assets
|
||||
uint256 prevBalI = IERC20(tokens[inputTokenIndex]).balanceOf(address(this));
|
||||
uint256 prevBalJ = IERC20(tokens[outputTokenIndex]).balanceOf(address(this));
|
||||
|
||||
// Compute amounts using the same path as views
|
||||
(uint256 totalTransferAmount, uint256 amountOutUint, int128 amountInInternalMax, int128 amountOutInternal, uint256 amountInUsedUint, uint256 feeUint) =
|
||||
_quoteSwapToLimit(inputTokenIndex, outputTokenIndex, limitPrice, swapFeePpm);
|
||||
|
||||
// Transfer the exact amount needed from payer and require exact receipt (revert on fee-on-transfer)
|
||||
tokens[inputTokenIndex].safeTransferFrom(payer, address(this), totalTransferAmount);
|
||||
uint256 balIAfter = IERC20(tokens[inputTokenIndex]).balanceOf(address(this));
|
||||
require(balIAfter == prevBalI + totalTransferAmount, "swapToLimit: non-standard tokenIn");
|
||||
|
||||
// Transfer output to receiver and verify exact decrease
|
||||
tokens[outputTokenIndex].safeTransfer(receiver, amountOutUint);
|
||||
uint256 balJAfter = IERC20(tokens[outputTokenIndex]).balanceOf(address(this));
|
||||
require(balJAfter == prevBalJ - amountOutUint, "swapToLimit: non-standard tokenOut");
|
||||
|
||||
// Accrue protocol share (floor) from the fee on input token
|
||||
if (protocolFeePpm > 0 && feeUint > 0 ) {
|
||||
uint256 protoShare = (feeUint * protocolFeePpm) / 1_000_000; // floor
|
||||
if (protoShare > 0) {
|
||||
protocolFeesOwed[inputTokenIndex] += protoShare;
|
||||
}
|
||||
}
|
||||
|
||||
// Update caches to effective balances
|
||||
_recordCachedBalance(inputTokenIndex, balIAfter);
|
||||
_recordCachedBalance(outputTokenIndex, balJAfter);
|
||||
|
||||
// Apply swap to LMSR state with the internal amounts
|
||||
lmsr.applySwap(inputTokenIndex, outputTokenIndex, amountInInternalMax, amountOutInternal);
|
||||
|
||||
// Maintain original event semantics (logs input without fee)
|
||||
emit IPartyPool.Swap(payer, receiver, tokens[inputTokenIndex], tokens[outputTokenIndex], amountInUsedUint, amountOutUint);
|
||||
|
||||
return (amountInUsedUint, amountOutUint, feeUint);
|
||||
}
|
||||
|
||||
|
||||
/// @notice Internal quote for swap-to-limit that mirrors swapToLimit() rounding and fee application
|
||||
/// @dev Computes the input required to reach limitPrice and the resulting output; all rounding matches swapToLimit.
|
||||
/// @return grossIn amount to transfer in (inclusive of fee), amountOutUint output amount (uint),
|
||||
/// amountInInternal and amountOutInternal (64.64), amountInUintNoFee input amount excluding fee (uint),
|
||||
/// feeUint fee taken from the gross input (uint)
|
||||
function _quoteSwapToLimit(
|
||||
uint256 inputTokenIndex,
|
||||
uint256 outputTokenIndex,
|
||||
int128 limitPrice,
|
||||
uint256 swapFeePpm
|
||||
) internal view
|
||||
returns (
|
||||
uint256 grossIn,
|
||||
uint256 amountOutUint,
|
||||
int128 amountInInternal,
|
||||
int128 amountOutInternal,
|
||||
uint256 amountInUintNoFee,
|
||||
uint256 feeUint
|
||||
)
|
||||
{
|
||||
// Compute internal maxima at the price limit
|
||||
(amountInInternal, amountOutInternal) = lmsr.swapAmountsForPriceLimit(inputTokenIndex, outputTokenIndex, limitPrice);
|
||||
|
||||
// Convert input to uint (ceil) and output to uint (floor)
|
||||
amountInUintNoFee = _internalToUintCeil(amountInInternal, bases[inputTokenIndex]);
|
||||
require(amountInUintNoFee > 0, "swapToLimit: input zero");
|
||||
|
||||
feeUint = 0;
|
||||
grossIn = amountInUintNoFee;
|
||||
if (swapFeePpm > 0) {
|
||||
feeUint = _ceilFee(amountInUintNoFee, swapFeePpm);
|
||||
grossIn += feeUint;
|
||||
}
|
||||
|
||||
amountOutUint = _internalToUintFloor(amountOutInternal, bases[outputTokenIndex]);
|
||||
require(amountOutUint > 0, "swapToLimit: output zero");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user