native currency fixes
This commit is contained in:
@@ -109,7 +109,7 @@ interface IPartyPool is IERC20Metadata {
|
|||||||
/// Can only be called when the pool is uninitialized (totalSupply() == 0 or lmsr.nAssets == 0).
|
/// Can only be called when the pool is uninitialized (totalSupply() == 0 or lmsr.nAssets == 0).
|
||||||
/// @param receiver address that receives the LP tokens
|
/// @param receiver address that receives the LP tokens
|
||||||
/// @param lpTokens The number of LP tokens to issue for this mint. If 0, then the number of tokens returned will equal the LMSR internal q total
|
/// @param lpTokens The number of LP tokens to issue for this mint. If 0, then the number of tokens returned will equal the LMSR internal q total
|
||||||
function initialMint(address receiver, uint256 lpTokens) external returns (uint256 lpMinted);
|
function initialMint(address receiver, uint256 lpTokens) external payable returns (uint256 lpMinted);
|
||||||
|
|
||||||
/// @notice Proportional mint (or initial supply if first call).
|
/// @notice Proportional mint (or initial supply if first call).
|
||||||
/// @dev - For initial supply: assumes tokens have already been transferred to the pool prior to calling.
|
/// @dev - For initial supply: assumes tokens have already been transferred to the pool prior to calling.
|
||||||
@@ -120,16 +120,16 @@ interface IPartyPool is IERC20Metadata {
|
|||||||
/// @param lpTokenAmount desired amount of LP tokens to mint (ignored for initial deposit)
|
/// @param lpTokenAmount desired amount of LP tokens to mint (ignored for initial deposit)
|
||||||
/// @param deadline timestamp after which the transaction will revert. Pass 0 to ignore.
|
/// @param deadline timestamp after which the transaction will revert. Pass 0 to ignore.
|
||||||
/// @return lpMinted the actual amount of lpToken minted
|
/// @return lpMinted the actual amount of lpToken minted
|
||||||
function mint(address payer, address receiver, uint256 lpTokenAmount, uint256 deadline) external returns (uint256 lpMinted);
|
function mint(address payer, address receiver, uint256 lpTokenAmount, uint256 deadline) external payable returns (uint256 lpMinted);
|
||||||
|
|
||||||
/// @notice Burn LP tokens and withdraw the proportional basket to receiver.
|
/// @notice Burn LP tokens and withdraw the proportional basket to receiver.
|
||||||
/// @dev Payer must own or approve the LP tokens being burned. The function updates LMSR state
|
/// @dev This function forwards the call to the burn implementation via delegatecall
|
||||||
/// proportionally to reflect the reduced pool size after the withdrawal.
|
|
||||||
/// @param payer address that provides the LP tokens to burn
|
/// @param payer address that provides the LP tokens to burn
|
||||||
/// @param receiver address that receives the withdrawn tokens
|
/// @param receiver address that receives the withdrawn tokens
|
||||||
/// @param lpAmount amount of LP tokens to burn (proportional withdrawal)
|
/// @param lpAmount amount of LP tokens to burn (proportional withdrawal)
|
||||||
/// @param deadline timestamp after which the transaction will revert. Pass 0 to ignore.
|
/// @param deadline timestamp after which the transaction will revert. Pass 0 to ignore.
|
||||||
function burn(address payer, address receiver, uint256 lpAmount, uint256 deadline) external returns (uint256[] memory withdrawAmounts);
|
/// @param unwrap if true and the native token is being withdrawn, it is unwraped and sent as native currency
|
||||||
|
function burn(address payer, address receiver, uint256 lpAmount, uint256 deadline, bool unwrap) external returns (uint256[] memory withdrawAmounts);
|
||||||
|
|
||||||
|
|
||||||
// Swaps
|
// Swaps
|
||||||
@@ -165,7 +165,8 @@ interface IPartyPool is IERC20Metadata {
|
|||||||
uint256 outputTokenIndex,
|
uint256 outputTokenIndex,
|
||||||
uint256 maxAmountIn,
|
uint256 maxAmountIn,
|
||||||
int128 limitPrice,
|
int128 limitPrice,
|
||||||
uint256 deadline
|
uint256 deadline,
|
||||||
|
bool unwrap
|
||||||
) external payable returns (uint256 amountIn, uint256 amountOut, uint256 fee);
|
) external payable returns (uint256 amountIn, uint256 amountOut, uint256 fee);
|
||||||
|
|
||||||
/// @notice Swap up to the price limit; computes max input to reach limit then performs swap.
|
/// @notice Swap up to the price limit; computes max input to reach limit then performs swap.
|
||||||
@@ -184,7 +185,8 @@ interface IPartyPool is IERC20Metadata {
|
|||||||
uint256 inputTokenIndex,
|
uint256 inputTokenIndex,
|
||||||
uint256 outputTokenIndex,
|
uint256 outputTokenIndex,
|
||||||
int128 limitPrice,
|
int128 limitPrice,
|
||||||
uint256 deadline
|
uint256 deadline,
|
||||||
|
bool unwrap
|
||||||
) external payable returns (uint256 amountInUsed, uint256 amountOut, uint256 fee);
|
) external payable returns (uint256 amountInUsed, uint256 amountOut, uint256 fee);
|
||||||
|
|
||||||
/// @notice Single-token mint: deposit a single token, charge swap-LMSR cost, and mint LP.
|
/// @notice Single-token mint: deposit a single token, charge swap-LMSR cost, and mint LP.
|
||||||
@@ -217,7 +219,8 @@ interface IPartyPool is IERC20Metadata {
|
|||||||
address receiver,
|
address receiver,
|
||||||
uint256 lpAmount,
|
uint256 lpAmount,
|
||||||
uint256 inputTokenIndex,
|
uint256 inputTokenIndex,
|
||||||
uint256 deadline
|
uint256 deadline,
|
||||||
|
bool unwrap
|
||||||
) external returns (uint256 amountOutUint);
|
) external returns (uint256 amountOutUint);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
|
|||||||
---------------------- */
|
---------------------- */
|
||||||
|
|
||||||
/// @inheritdoc IPartyPool
|
/// @inheritdoc IPartyPool
|
||||||
function initialMint(address receiver, uint256 lpTokens) external
|
function initialMint(address receiver, uint256 lpTokens) external payable
|
||||||
returns (uint256 lpMinted) {
|
returns (uint256 lpMinted) {
|
||||||
bytes memory data = abi.encodeWithSelector(
|
bytes memory data = abi.encodeWithSelector(
|
||||||
PartyPoolMintImpl.initialMint.selector,
|
PartyPoolMintImpl.initialMint.selector,
|
||||||
@@ -169,7 +169,7 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
|
|||||||
/// @param receiver address that receives the LP tokens
|
/// @param receiver address that receives the LP tokens
|
||||||
/// @param lpTokenAmount desired amount of LP tokens to mint
|
/// @param lpTokenAmount desired amount of LP tokens to mint
|
||||||
/// @param deadline timestamp after which the transaction will revert. Pass 0 to ignore.
|
/// @param deadline timestamp after which the transaction will revert. Pass 0 to ignore.
|
||||||
function mint(address payer, address receiver, uint256 lpTokenAmount, uint256 deadline) external
|
function mint(address payer, address receiver, uint256 lpTokenAmount, uint256 deadline) external payable
|
||||||
returns (uint256 lpMinted) {
|
returns (uint256 lpMinted) {
|
||||||
bytes memory data = abi.encodeWithSelector(
|
bytes memory data = abi.encodeWithSelector(
|
||||||
PartyPoolMintImpl.mint.selector,
|
PartyPoolMintImpl.mint.selector,
|
||||||
@@ -182,20 +182,16 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
|
|||||||
return abi.decode(result, (uint256));
|
return abi.decode(result, (uint256));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @notice Burn LP tokens and withdraw the proportional basket to receiver.
|
/// @inheritdoc IPartyPool
|
||||||
/// @dev This function forwards the call to the burn implementation via delegatecall
|
function burn(address payer, address receiver, uint256 lpAmount, uint256 deadline, bool unwrap) external
|
||||||
/// @param payer address that provides the LP tokens to burn
|
|
||||||
/// @param receiver address that receives the withdrawn tokens
|
|
||||||
/// @param lpAmount amount of LP tokens to burn (proportional withdrawal)
|
|
||||||
/// @param deadline timestamp after which the transaction will revert. Pass 0 to ignore.
|
|
||||||
function burn(address payer, address receiver, uint256 lpAmount, uint256 deadline) external
|
|
||||||
returns (uint256[] memory withdrawAmounts) {
|
returns (uint256[] memory withdrawAmounts) {
|
||||||
bytes memory data = abi.encodeWithSelector(
|
bytes memory data = abi.encodeWithSelector(
|
||||||
PartyPoolMintImpl.burn.selector,
|
PartyPoolMintImpl.burn.selector,
|
||||||
payer,
|
payer,
|
||||||
receiver,
|
receiver,
|
||||||
lpAmount,
|
lpAmount,
|
||||||
deadline
|
deadline,
|
||||||
|
unwrap
|
||||||
);
|
);
|
||||||
bytes memory result = Address.functionDelegateCall(address(MINT_IMPL), data);
|
bytes memory result = Address.functionDelegateCall(address(MINT_IMPL), data);
|
||||||
return abi.decode(result, (uint256[]));
|
return abi.decode(result, (uint256[]));
|
||||||
@@ -223,8 +219,9 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
|
|||||||
uint256 outputTokenIndex,
|
uint256 outputTokenIndex,
|
||||||
uint256 maxAmountIn,
|
uint256 maxAmountIn,
|
||||||
int128 limitPrice,
|
int128 limitPrice,
|
||||||
uint256 deadline
|
uint256 deadline,
|
||||||
) external payable nonReentrant returns (uint256 amountIn, uint256 amountOut, uint256 fee) {
|
bool unwrap
|
||||||
|
) external payable native nonReentrant returns (uint256 amountIn, uint256 amountOut, uint256 fee) {
|
||||||
require(deadline == 0 || block.timestamp <= deadline, "swap: deadline exceeded");
|
require(deadline == 0 || block.timestamp <= deadline, "swap: deadline exceeded");
|
||||||
|
|
||||||
// Compute amounts using the same path as views
|
// Compute amounts using the same path as views
|
||||||
@@ -243,7 +240,7 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
|
|||||||
uint256 balJAfter = cachedUintBalances[outputTokenIndex] + protocolFeesOwed[outputTokenIndex] - amountOutUint;
|
uint256 balJAfter = cachedUintBalances[outputTokenIndex] + protocolFeesOwed[outputTokenIndex] - amountOutUint;
|
||||||
|
|
||||||
// Transfer output to receiver via centralized helper
|
// Transfer output to receiver via centralized helper
|
||||||
_sendTokenTo(tokenOut, receiver, amountOutUint);
|
_sendTokenTo(tokenOut, receiver, amountOutUint, unwrap);
|
||||||
|
|
||||||
// Accrue protocol share (floor) from the fee on input token
|
// Accrue protocol share (floor) from the fee on input token
|
||||||
if (PROTOCOL_FEE_PPM > 0 && feeUint > 0) {
|
if (PROTOCOL_FEE_PPM > 0 && feeUint > 0) {
|
||||||
@@ -265,8 +262,6 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
|
|||||||
|
|
||||||
emit Swap(payer, receiver, tokenIn, tokenOut, totalTransferAmount, amountOutUint);
|
emit Swap(payer, receiver, tokenIn, tokenOut, totalTransferAmount, amountOutUint);
|
||||||
|
|
||||||
_refund();
|
|
||||||
|
|
||||||
return (totalTransferAmount, amountOutUint, feeUint);
|
return (totalTransferAmount, amountOutUint, feeUint);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,6 +297,7 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
|
|||||||
require(deltaInternalI > int128(0), "swap: input too small after fee");
|
require(deltaInternalI > int128(0), "swap: input too small after fee");
|
||||||
|
|
||||||
// Compute internal amounts using LMSR (exact-input with price limit)
|
// Compute internal amounts using LMSR (exact-input with price limit)
|
||||||
|
// use the virtual method call so that the balanced pair optimization can override
|
||||||
(amountInInternalUsed, amountOutInternal) = _swapAmountsForExactInput(inputTokenIndex, outputTokenIndex, deltaInternalI, limitPrice);
|
(amountInInternalUsed, amountOutInternal) = _swapAmountsForExactInput(inputTokenIndex, outputTokenIndex, deltaInternalI, limitPrice);
|
||||||
|
|
||||||
// Convert actual used input internal -> uint (ceil)
|
// Convert actual used input internal -> uint (ceil)
|
||||||
@@ -330,7 +326,8 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
|
|||||||
uint256 inputTokenIndex,
|
uint256 inputTokenIndex,
|
||||||
uint256 outputTokenIndex,
|
uint256 outputTokenIndex,
|
||||||
int128 limitPrice,
|
int128 limitPrice,
|
||||||
uint256 deadline
|
uint256 deadline,
|
||||||
|
bool unwrap
|
||||||
) external payable returns (uint256 amountInUsed, uint256 amountOut, uint256 fee) {
|
) external payable returns (uint256 amountInUsed, uint256 amountOut, uint256 fee) {
|
||||||
bytes memory data = abi.encodeWithSelector(
|
bytes memory data = abi.encodeWithSelector(
|
||||||
PartyPoolSwapImpl.swapToLimit.selector,
|
PartyPoolSwapImpl.swapToLimit.selector,
|
||||||
@@ -340,6 +337,7 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
|
|||||||
outputTokenIndex,
|
outputTokenIndex,
|
||||||
limitPrice,
|
limitPrice,
|
||||||
deadline,
|
deadline,
|
||||||
|
unwrap,
|
||||||
SWAP_FEE_PPM,
|
SWAP_FEE_PPM,
|
||||||
PROTOCOL_FEE_PPM
|
PROTOCOL_FEE_PPM
|
||||||
);
|
);
|
||||||
@@ -391,7 +389,8 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
|
|||||||
address receiver,
|
address receiver,
|
||||||
uint256 lpAmount,
|
uint256 lpAmount,
|
||||||
uint256 inputTokenIndex,
|
uint256 inputTokenIndex,
|
||||||
uint256 deadline
|
uint256 deadline,
|
||||||
|
bool unwrap
|
||||||
) external returns (uint256 amountOutUint) {
|
) external returns (uint256 amountOutUint) {
|
||||||
bytes memory data = abi.encodeWithSelector(
|
bytes memory data = abi.encodeWithSelector(
|
||||||
PartyPoolMintImpl.burnSwap.selector,
|
PartyPoolMintImpl.burnSwap.selector,
|
||||||
@@ -400,6 +399,7 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
|
|||||||
lpAmount,
|
lpAmount,
|
||||||
inputTokenIndex,
|
inputTokenIndex,
|
||||||
deadline,
|
deadline,
|
||||||
|
unwrap,
|
||||||
SWAP_FEE_PPM,
|
SWAP_FEE_PPM,
|
||||||
PROTOCOL_FEE_PPM
|
PROTOCOL_FEE_PPM
|
||||||
);
|
);
|
||||||
@@ -438,7 +438,7 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_sendTokenTo(token, address(receiver), amount);
|
_sendTokenTo(token, address(receiver), amount, false);
|
||||||
require(receiver.onFlashLoan(msg.sender, address(token), amount, fee, data) == FLASH_CALLBACK_SUCCESS);
|
require(receiver.onFlashLoan(msg.sender, address(token), amount, fee, data) == FLASH_CALLBACK_SUCCESS);
|
||||||
_receiveTokenFrom(address(receiver), token, amount + fee);
|
_receiveTokenFrom(address(receiver), token, amount + fee);
|
||||||
|
|
||||||
@@ -466,7 +466,7 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
|
|||||||
require(bal >= owed, "collect: fee > bal");
|
require(bal >= owed, "collect: fee > bal");
|
||||||
protocolFeesOwed[i] = 0;
|
protocolFeesOwed[i] = 0;
|
||||||
// transfer owed tokens to protocol destination via centralized helper
|
// transfer owed tokens to protocol destination via centralized helper
|
||||||
_sendTokenTo(tokens[i], dest, owed);
|
_sendTokenTo(tokens[i], dest, owed, false);
|
||||||
// update cached to effective onchain minus owed
|
// update cached to effective onchain minus owed
|
||||||
cachedUintBalances[i] = bal - owed;
|
cachedUintBalances[i] = bal - owed;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,15 @@ abstract contract PartyPoolBase is ERC20Internal, ReentrancyGuard, PartyPoolHelp
|
|||||||
WRAPPER_TOKEN = wrapper_;
|
WRAPPER_TOKEN = wrapper_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @notice Designates methods that can receive native currency.
|
||||||
|
/// @dev If the pool has any balance of native currency at the end of the method, it is refunded to msg.sender
|
||||||
|
modifier native() {
|
||||||
|
_;
|
||||||
|
uint256 bal = address(this).balance;
|
||||||
|
if(bal > 0)
|
||||||
|
payable(msg.sender).transfer(bal);
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Internal state (no immutables here; immutables belong to derived contracts)
|
// Internal state (no immutables here; immutables belong to derived contracts)
|
||||||
//
|
//
|
||||||
@@ -104,19 +113,14 @@ abstract contract PartyPoolBase is ERC20Internal, ReentrancyGuard, PartyPoolHelp
|
|||||||
/// @notice Send tokens from the pool to `receiver` using SafeERC20 semantics.
|
/// @notice Send tokens from the pool to `receiver` using SafeERC20 semantics.
|
||||||
/// @dev Note: this helper does NOT query the on-chain balance after transfer to save gas.
|
/// @dev Note: this helper does NOT query the on-chain balance after transfer to save gas.
|
||||||
/// Callers should query the balance themselves when they need it (e.g., to detect fee-on-transfer tokens).
|
/// Callers should query the balance themselves when they need it (e.g., to detect fee-on-transfer tokens).
|
||||||
function _sendTokenTo(IERC20 token, address receiver, uint256 amount) internal {
|
function _sendTokenTo(IERC20 token, address receiver, uint256 amount, bool unwrap) internal {
|
||||||
if( token == WRAPPER_TOKEN ) {
|
if( unwrap && token == WRAPPER_TOKEN ) {
|
||||||
WRAPPER_TOKEN.withdraw(amount);
|
WRAPPER_TOKEN.withdraw(amount);
|
||||||
(bool ok, ) = receiver.call{value: amount}("");
|
(bool ok, ) = receiver.call{value: amount}("");
|
||||||
require(ok); // todo make unwrapping optional
|
require(ok, 'receiver not payable');
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
token.safeTransfer(receiver, amount);
|
token.safeTransfer(receiver, amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _refund() internal {
|
|
||||||
uint256 bal = address(this).balance;
|
|
||||||
if(bal > 0)
|
|
||||||
payable(msg.sender).transfer(bal);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
|||||||
// Initialization Mint
|
// Initialization Mint
|
||||||
//
|
//
|
||||||
|
|
||||||
function initialMint(address receiver, uint256 lpTokens, int128 KAPPA) external nonReentrant
|
function initialMint(address receiver, uint256 lpTokens, int128 KAPPA) external payable native nonReentrant
|
||||||
returns (uint256 lpMinted) {
|
returns (uint256 lpMinted) {
|
||||||
uint256 n = tokens.length;
|
uint256 n = tokens.length;
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
|||||||
// Regular Mint and Burn
|
// Regular Mint and Burn
|
||||||
//
|
//
|
||||||
|
|
||||||
function mint(address payer, address receiver, uint256 lpTokenAmount, uint256 deadline) external payable nonReentrant
|
function mint(address payer, address receiver, uint256 lpTokenAmount, uint256 deadline) external payable native nonReentrant
|
||||||
returns (uint256 lpMinted) {
|
returns (uint256 lpMinted) {
|
||||||
require(deadline == 0 || block.timestamp <= deadline, "mint: deadline exceeded");
|
require(deadline == 0 || block.timestamp <= deadline, "mint: deadline exceeded");
|
||||||
uint256 n = tokens.length;
|
uint256 n = tokens.length;
|
||||||
@@ -128,8 +128,6 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
|||||||
_mint(receiver, actualLpToMint);
|
_mint(receiver, actualLpToMint);
|
||||||
emit IPartyPool.Mint(payer, receiver, depositAmounts, actualLpToMint);
|
emit IPartyPool.Mint(payer, receiver, depositAmounts, actualLpToMint);
|
||||||
|
|
||||||
_refund();
|
|
||||||
|
|
||||||
return actualLpToMint;
|
return actualLpToMint;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,7 +138,8 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
|||||||
/// @param receiver address that receives the withdrawn tokens
|
/// @param receiver address that receives the withdrawn tokens
|
||||||
/// @param lpAmount amount of LP tokens to burn (proportional withdrawal)
|
/// @param lpAmount amount of LP tokens to burn (proportional withdrawal)
|
||||||
/// @param deadline timestamp after which the transaction will revert. Pass 0 to ignore.
|
/// @param deadline timestamp after which the transaction will revert. Pass 0 to ignore.
|
||||||
function burn(address payer, address receiver, uint256 lpAmount, uint256 deadline) external nonReentrant
|
/// @param unwrap if true and the native token is being withdrawn, it is unwraped and sent as native currency
|
||||||
|
function burn(address payer, address receiver, uint256 lpAmount, uint256 deadline, bool unwrap) external nonReentrant
|
||||||
returns (uint256[] memory withdrawAmounts) {
|
returns (uint256[] memory withdrawAmounts) {
|
||||||
require(deadline == 0 || block.timestamp <= deadline, "burn: deadline exceeded");
|
require(deadline == 0 || block.timestamp <= deadline, "burn: deadline exceeded");
|
||||||
uint256 n = tokens.length;
|
uint256 n = tokens.length;
|
||||||
@@ -157,7 +156,7 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
|||||||
// Transfer underlying tokens out to receiver according to computed proportions
|
// Transfer underlying tokens out to receiver according to computed proportions
|
||||||
for (uint i = 0; i < n; ) {
|
for (uint i = 0; i < n; ) {
|
||||||
if (withdrawAmounts[i] > 0) {
|
if (withdrawAmounts[i] > 0) {
|
||||||
_sendTokenTo(tokens[i], receiver, withdrawAmounts[i]);
|
_sendTokenTo(tokens[i], receiver, withdrawAmounts[i], unwrap);
|
||||||
}
|
}
|
||||||
unchecked { i++; }
|
unchecked { i++; }
|
||||||
}
|
}
|
||||||
@@ -346,7 +345,7 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
|||||||
uint256 deadline,
|
uint256 deadline,
|
||||||
uint256 swapFeePpm,
|
uint256 swapFeePpm,
|
||||||
uint256 protocolFeePpm
|
uint256 protocolFeePpm
|
||||||
) external nonReentrant returns (uint256 lpMinted) {
|
) external payable native nonReentrant returns (uint256 lpMinted) {
|
||||||
uint256 n = tokens.length;
|
uint256 n = tokens.length;
|
||||||
require(inputTokenIndex < n, "swapMint: idx");
|
require(inputTokenIndex < n, "swapMint: idx");
|
||||||
require(maxAmountIn > 0, "swapMint: input zero");
|
require(maxAmountIn > 0, "swapMint: input zero");
|
||||||
@@ -483,6 +482,7 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
|||||||
uint256 lpAmount,
|
uint256 lpAmount,
|
||||||
uint256 inputTokenIndex,
|
uint256 inputTokenIndex,
|
||||||
uint256 deadline,
|
uint256 deadline,
|
||||||
|
bool unwrap,
|
||||||
uint256 swapFeePpm,
|
uint256 swapFeePpm,
|
||||||
uint256 protocolFeePpm
|
uint256 protocolFeePpm
|
||||||
) external nonReentrant returns (uint256 amountOutUint) {
|
) external nonReentrant returns (uint256 amountOutUint) {
|
||||||
@@ -521,7 +521,7 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Transfer the payout to receiver via centralized helper
|
// Transfer the payout to receiver via centralized helper
|
||||||
_sendTokenTo(tokens[inputTokenIndex], receiver, amountOutUint);
|
_sendTokenTo(tokens[inputTokenIndex], receiver, amountOutUint, unwrap);
|
||||||
|
|
||||||
// Burn LP tokens from payer (authorization via allowance)
|
// Burn LP tokens from payer (authorization via allowance)
|
||||||
if (msg.sender != payer) {
|
if (msg.sender != payer) {
|
||||||
|
|||||||
@@ -56,9 +56,10 @@ contract PartyPoolSwapImpl is PartyPoolBase {
|
|||||||
uint256 outputTokenIndex,
|
uint256 outputTokenIndex,
|
||||||
int128 limitPrice,
|
int128 limitPrice,
|
||||||
uint256 deadline,
|
uint256 deadline,
|
||||||
|
bool unwrap,
|
||||||
uint256 swapFeePpm,
|
uint256 swapFeePpm,
|
||||||
uint256 protocolFeePpm
|
uint256 protocolFeePpm
|
||||||
) external payable returns (uint256 amountInUsed, uint256 amountOut, uint256 fee) {
|
) external payable native returns (uint256 amountInUsed, uint256 amountOut, uint256 fee) {
|
||||||
uint256 n = tokens.length;
|
uint256 n = tokens.length;
|
||||||
require(inputTokenIndex < n && outputTokenIndex < n, "swapToLimit: idx");
|
require(inputTokenIndex < n && outputTokenIndex < n, "swapToLimit: idx");
|
||||||
require(limitPrice > int128(0), "swapToLimit: limit <= 0");
|
require(limitPrice > int128(0), "swapToLimit: limit <= 0");
|
||||||
@@ -78,7 +79,7 @@ contract PartyPoolSwapImpl is PartyPoolBase {
|
|||||||
require(balIAfter == prevBalI + totalTransferAmount, "swapToLimit: non-standard tokenIn");
|
require(balIAfter == prevBalI + totalTransferAmount, "swapToLimit: non-standard tokenIn");
|
||||||
|
|
||||||
// Transfer output to receiver and verify exact decrease
|
// Transfer output to receiver and verify exact decrease
|
||||||
_sendTokenTo(tokens[outputTokenIndex], receiver, amountOutUint);
|
_sendTokenTo(tokens[outputTokenIndex], receiver, amountOutUint, unwrap);
|
||||||
uint256 balJAfter = IERC20(tokens[outputTokenIndex]).balanceOf(address(this));
|
uint256 balJAfter = IERC20(tokens[outputTokenIndex]).balanceOf(address(this));
|
||||||
require(balJAfter == prevBalJ - amountOutUint, "swapToLimit: non-standard tokenOut");
|
require(balJAfter == prevBalJ - amountOutUint, "swapToLimit: non-standard tokenOut");
|
||||||
|
|
||||||
@@ -103,8 +104,6 @@ contract PartyPoolSwapImpl is PartyPoolBase {
|
|||||||
// Maintain original event semantics (logs input without fee)
|
// Maintain original event semantics (logs input without fee)
|
||||||
emit IPartyPool.Swap(payer, receiver, tokens[inputTokenIndex], tokens[outputTokenIndex], amountInUsedUint, amountOutUint);
|
emit IPartyPool.Swap(payer, receiver, tokens[inputTokenIndex], tokens[outputTokenIndex], amountInUsedUint, amountOutUint);
|
||||||
|
|
||||||
_refund();
|
|
||||||
|
|
||||||
return (amountInUsedUint, amountOutUint, feeUint);
|
return (amountInUsedUint, amountOutUint, feeUint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -244,10 +244,10 @@ contract GasTest is Test {
|
|||||||
vm.prank(alice);
|
vm.prank(alice);
|
||||||
if (i % 2 == 0) {
|
if (i % 2 == 0) {
|
||||||
// swap token0 -> token1
|
// swap token0 -> token1
|
||||||
testPool.swap(alice, alice, 0, 1, maxIn, 0, 0);
|
testPool.swap(alice, alice, 0, 1, maxIn, 0, 0, false);
|
||||||
} else {
|
} else {
|
||||||
// swap token1 -> token0
|
// swap token1 -> token0
|
||||||
testPool.swap(alice, alice, 1, 0, maxIn, 0, 0);
|
testPool.swap(alice, alice, 1, 0, maxIn, 0, 0, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -308,7 +308,7 @@ contract GasTest is Test {
|
|||||||
// If nothing minted (numerical edge), skip burn step
|
// If nothing minted (numerical edge), skip burn step
|
||||||
if (minted == 0) continue;
|
if (minted == 0) continue;
|
||||||
// Immediately burn the minted LP back to tokens, targeting the same token index
|
// Immediately burn the minted LP back to tokens, targeting the same token index
|
||||||
testPool.burnSwap(alice, alice, minted, 0, 0);
|
testPool.burnSwap(alice, alice, minted, 0, 0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.stopPrank();
|
vm.stopPrank();
|
||||||
@@ -368,7 +368,7 @@ contract GasTest is Test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Burn via plain burn() which will transfer underlying back to alice and burn LP
|
// Burn via plain burn() which will transfer underlying back to alice and burn LP
|
||||||
testPool.burn(alice, alice, actualMinted, 0);
|
testPool.burn(alice, alice, actualMinted, 0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.stopPrank();
|
vm.stopPrank();
|
||||||
|
|||||||
@@ -408,7 +408,7 @@ contract PartyPoolTest is Test {
|
|||||||
|
|
||||||
// Burn by sending LP tokens from this contract (which holds initial LP from setUp)
|
// Burn by sending LP tokens from this contract (which holds initial LP from setUp)
|
||||||
// Call burn(payer=this, receiver=bob, lpAmount=totalLp)
|
// Call burn(payer=this, receiver=bob, lpAmount=totalLp)
|
||||||
pool.burn(address(this), bob, totalLp, 0);
|
pool.burn(address(this), bob, totalLp, 0, false);
|
||||||
|
|
||||||
// After burning entire pool, totalSupply should be zero or very small (we expect zero since we withdrew all)
|
// After burning entire pool, totalSupply should be zero or very small (we expect zero since we withdrew all)
|
||||||
assertEq(pool.totalSupply(), 0);
|
assertEq(pool.totalSupply(), 0);
|
||||||
@@ -434,7 +434,7 @@ contract PartyPoolTest is Test {
|
|||||||
|
|
||||||
// Execute swap: token0 -> token1
|
// Execute swap: token0 -> token1
|
||||||
vm.prank(alice);
|
vm.prank(alice);
|
||||||
(uint256 amountInUsed, uint256 amountOut, uint256 fee) = pool.swap(alice, bob, 0, 1, maxIn, 0, 0);
|
(uint256 amountInUsed, uint256 amountOut, uint256 fee) = pool.swap(alice, bob, 0, 1, maxIn, 0, 0, false);
|
||||||
|
|
||||||
// Amounts should be positive and not exceed provided max
|
// Amounts should be positive and not exceed provided max
|
||||||
assertTrue(amountInUsed > 0, "expected some input used");
|
assertTrue(amountInUsed > 0, "expected some input used");
|
||||||
@@ -463,7 +463,7 @@ contract PartyPoolTest is Test {
|
|||||||
|
|
||||||
vm.prank(alice);
|
vm.prank(alice);
|
||||||
vm.expectRevert(bytes("LMSR: limitPrice <= current price"));
|
vm.expectRevert(bytes("LMSR: limitPrice <= current price"));
|
||||||
pool.swap(alice, alice, 0, 1, 1000, limitPrice, 0);
|
pool.swap(alice, alice, 0, 1, 1000, limitPrice, 0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @notice swapToLimit should compute input needed to reach a slightly higher price and execute.
|
/// @notice swapToLimit should compute input needed to reach a slightly higher price and execute.
|
||||||
@@ -475,7 +475,7 @@ contract PartyPoolTest is Test {
|
|||||||
token0.approve(address(pool), type(uint256).max);
|
token0.approve(address(pool), type(uint256).max);
|
||||||
|
|
||||||
vm.prank(alice);
|
vm.prank(alice);
|
||||||
(uint256 amountInUsed, uint256 amountOut, uint256 fee) = pool.swapToLimit(alice, bob, 0, 1, limitPrice, 0);
|
(uint256 amountInUsed, uint256 amountOut, uint256 fee) = pool.swapToLimit(alice, bob, 0, 1, limitPrice, 0, false);
|
||||||
|
|
||||||
assertTrue(amountInUsed > 0, "expected some input used for swapToLimit");
|
assertTrue(amountInUsed > 0, "expected some input used for swapToLimit");
|
||||||
assertTrue(amountOut > 0, "expected some output for swapToLimit");
|
assertTrue(amountOut > 0, "expected some output for swapToLimit");
|
||||||
@@ -637,7 +637,7 @@ contract PartyPoolTest is Test {
|
|||||||
uint256 b2Before = token2.balanceOf(bob);
|
uint256 b2Before = token2.balanceOf(bob);
|
||||||
|
|
||||||
// Perform burn using the computed LP amount (proportional withdrawal)
|
// Perform burn using the computed LP amount (proportional withdrawal)
|
||||||
pool.burn(address(this), bob, req, 0);
|
pool.burn(address(this), bob, req, 0, false);
|
||||||
|
|
||||||
// Verify bob received exactly the expected amounts
|
// Verify bob received exactly the expected amounts
|
||||||
assertEq(token0.balanceOf(bob) - b0Before, expected[0], "token0 withdraw mismatch");
|
assertEq(token0.balanceOf(bob) - b0Before, expected[0], "token0 withdraw mismatch");
|
||||||
@@ -701,7 +701,7 @@ contract PartyPoolTest is Test {
|
|||||||
beforeBal[8] = token8.balanceOf(bob);
|
beforeBal[8] = token8.balanceOf(bob);
|
||||||
beforeBal[9] = token9.balanceOf(bob);
|
beforeBal[9] = token9.balanceOf(bob);
|
||||||
|
|
||||||
pool10.burn(address(this), bob, req, 0);
|
pool10.burn(address(this), bob, req, 0, false);
|
||||||
|
|
||||||
// Verify bob received each expected amount
|
// Verify bob received each expected amount
|
||||||
assertEq(token0.balanceOf(bob) - beforeBal[0], expected[0], "t0 withdraw mismatch");
|
assertEq(token0.balanceOf(bob) - beforeBal[0], expected[0], "t0 withdraw mismatch");
|
||||||
@@ -795,7 +795,7 @@ contract PartyPoolTest is Test {
|
|||||||
uint256 bobBefore = token0.balanceOf(bob);
|
uint256 bobBefore = token0.balanceOf(bob);
|
||||||
|
|
||||||
// Call burnSwap where this contract is the payer (it holds initial LP from setUp)
|
// Call burnSwap where this contract is the payer (it holds initial LP from setUp)
|
||||||
uint256 payout = pool.burnSwap(address(this), bob, lpToBurn, target, 0);
|
uint256 payout = pool.burnSwap(address(this), bob, lpToBurn, target, 0, false);
|
||||||
|
|
||||||
// Payout must be > 0
|
// Payout must be > 0
|
||||||
assertTrue(payout > 0, "burnSwap should produce a payout");
|
assertTrue(payout > 0, "burnSwap should produce a payout");
|
||||||
@@ -1040,8 +1040,8 @@ contract PartyPoolTest is Test {
|
|||||||
token0.approve(address(poolCustom), type(uint256).max);
|
token0.approve(address(poolCustom), type(uint256).max);
|
||||||
|
|
||||||
// Perform identical swaps: token0 -> token1
|
// Perform identical swaps: token0 -> token1
|
||||||
(uint256 amountInDefault, uint256 amountOutDefault, uint256 feeDefault) = poolDefault.swap(alice, alice, 0, 1, swapAmount, 0, 0);
|
(uint256 amountInDefault, uint256 amountOutDefault, uint256 feeDefault) = poolDefault.swap(alice, alice, 0, 1, swapAmount, 0, 0, false);
|
||||||
(uint256 amountInCustom, uint256 amountOutCustom, uint256 feeCustom) = poolCustom.swap(alice, alice, 0, 1, swapAmount, 0, 0);
|
(uint256 amountInCustom, uint256 amountOutCustom, uint256 feeCustom) = poolCustom.swap(alice, alice, 0, 1, swapAmount, 0, 0, false);
|
||||||
|
|
||||||
// Swap results should be identical
|
// Swap results should be identical
|
||||||
assertEq(amountInDefault, amountInCustom, "Swap input amounts should be identical");
|
assertEq(amountInDefault, amountInCustom, "Swap input amounts should be identical");
|
||||||
|
|||||||
Reference in New Issue
Block a user