delegated reentrancy lock
This commit is contained in:
@@ -9,7 +9,7 @@ remappings = [
|
||||
optimizer=true
|
||||
optimizer_runs=999999999
|
||||
viaIR=true
|
||||
gas_reports = ['PartyPool', 'PartyPlanner', 'PartyPoolSwapMintImpl', 'PartyPoolMintImpl',]
|
||||
gas_reports = ['PartyPool', 'PartyPlanner', 'PartyPoolSwapImpl', 'PartyPoolMintImpl',]
|
||||
fs_permissions = [{ access = "write", path = "chain.json"}]
|
||||
|
||||
[lint]
|
||||
|
||||
@@ -144,7 +144,7 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
|
||||
---------------------- */
|
||||
|
||||
/// @inheritdoc IPartyPool
|
||||
function initialMint(address receiver, uint256 lpTokens) external nonReentrant
|
||||
function initialMint(address receiver, uint256 lpTokens) external
|
||||
returns (uint256 lpMinted) {
|
||||
bytes memory data = abi.encodeWithSignature(
|
||||
"initialMint(address,uint256,int128)",
|
||||
@@ -162,7 +162,7 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
|
||||
/// @param receiver address that receives the LP tokens
|
||||
/// @param lpTokenAmount desired amount of LP tokens to mint
|
||||
/// @param deadline timestamp after which the transaction will revert. Pass 0 to ignore.
|
||||
function mint(address payer, address receiver, uint256 lpTokenAmount, uint256 deadline) external nonReentrant
|
||||
function mint(address payer, address receiver, uint256 lpTokenAmount, uint256 deadline) external
|
||||
returns (uint256 lpMinted) {
|
||||
bytes memory data = abi.encodeWithSignature(
|
||||
"mint(address,address,uint256,uint256)",
|
||||
@@ -181,7 +181,7 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
|
||||
/// @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 nonReentrant
|
||||
function burn(address payer, address receiver, uint256 lpAmount, uint256 deadline) external
|
||||
returns (uint256[] memory withdrawAmounts) {
|
||||
bytes memory data = abi.encodeWithSignature(
|
||||
"burn(address,address,uint256,uint256)",
|
||||
@@ -220,31 +220,20 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
|
||||
int128 limitPrice,
|
||||
uint256 deadline
|
||||
) external nonReentrant returns (uint256 amountIn, uint256 amountOut, uint256 fee) {
|
||||
uint256 n = tokens.length;
|
||||
require(inputTokenIndex < n && outputTokenIndex < n, "swap: idx");
|
||||
require(maxAmountIn > 0, "swap: input zero");
|
||||
require(deadline == 0 || block.timestamp <= deadline, "swap: 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 amountInInternalUsed, int128 amountOutInternal, , uint256 feeUint) =
|
||||
_quoteSwapExactIn(inputTokenIndex, outputTokenIndex, maxAmountIn, limitPrice);
|
||||
|
||||
// Transfer the exact amount from payer and require exact receipt (revert on fee-on-transfer)
|
||||
// Transfer tokens
|
||||
tokens[inputTokenIndex].safeTransferFrom(payer, address(this), totalTransferAmount);
|
||||
uint256 balIAfter = IERC20(tokens[inputTokenIndex]).balanceOf(address(this));
|
||||
require(balIAfter == prevBalI + totalTransferAmount, "swap: 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, "swap: non-standard tokenOut");
|
||||
|
||||
// Accrue protocol share (floor) from the fee on input token
|
||||
if (PROTOCOL_FEE_PPM > 0 && feeUint > 0 && PROTOCOL_FEE_ADDRESS != address(0)) {
|
||||
if (PROTOCOL_FEE_PPM > 0 && feeUint > 0) {
|
||||
uint256 protoShare = (feeUint * PROTOCOL_FEE_PPM) / 1_000_000; // floor
|
||||
if (protoShare > 0) {
|
||||
protocolFeesOwed[inputTokenIndex] += protoShare;
|
||||
@@ -288,7 +277,6 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
|
||||
uint256 n = tokens.length;
|
||||
require(inputTokenIndex < n && outputTokenIndex < n, "swap: idx");
|
||||
require(maxAmountIn > 0, "swap: input zero");
|
||||
require(lmsr.nAssets > 0, "swap: empty pool");
|
||||
|
||||
// Estimate max net input (fee on gross rounded up, then subtract)
|
||||
(, uint256 netUintForSwap) = _computeFee(maxAmountIn, SWAP_FEE_PPM);
|
||||
@@ -302,7 +290,6 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
|
||||
|
||||
// Convert actual used input internal -> uint (ceil)
|
||||
amountInUintNoFee = _internalToUintCeil(amountInInternalUsed, bases[inputTokenIndex]);
|
||||
require(amountInUintNoFee > 0, "swap: input zero");
|
||||
|
||||
// Compute gross transfer including fee on the used input (ceil)
|
||||
feeUint = 0;
|
||||
@@ -317,7 +304,6 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
|
||||
|
||||
// Compute output (floor)
|
||||
amountOutUint = _internalToUintFloor(amountOutInternal, bases[outputTokenIndex]);
|
||||
require(amountOutUint > 0, "swap: output zero");
|
||||
}
|
||||
|
||||
|
||||
@@ -329,7 +315,7 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
|
||||
uint256 outputTokenIndex,
|
||||
int128 limitPrice,
|
||||
uint256 deadline
|
||||
) external nonReentrant returns (uint256 amountInUsed, uint256 amountOut, uint256 fee) {
|
||||
) external returns (uint256 amountInUsed, uint256 amountOut, uint256 fee) {
|
||||
bytes memory data = abi.encodeWithSignature(
|
||||
'swapToLimit(address,address,uint256,uint256,int128,uint256,uint256,uint256)',
|
||||
payer,
|
||||
@@ -426,18 +412,9 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
|
||||
IERC20 token = IERC20(tokenAddr);
|
||||
require(amount <= token.balanceOf(address(this)));
|
||||
(uint256 fee, ) = _computeFee(amount, FLASH_FEE_PPM);
|
||||
require(
|
||||
token.transfer(address(receiver), amount),
|
||||
"FlashLender: Transfer failed"
|
||||
);
|
||||
require(
|
||||
receiver.onFlashLoan(msg.sender, address(token), amount, fee, data) == FLASH_CALLBACK_SUCCESS,
|
||||
"FlashLender: Callback failed"
|
||||
);
|
||||
require(
|
||||
token.transferFrom(address(receiver), address(this), amount + fee),
|
||||
"FlashLender: Repay failed"
|
||||
);
|
||||
require(token.transfer(address(receiver), amount));
|
||||
require(receiver.onFlashLoan(msg.sender, address(token), amount, fee, data) == FLASH_CALLBACK_SUCCESS);
|
||||
require(token.transferFrom(address(receiver), address(this), amount + fee));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -467,15 +444,4 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
|
||||
return lmsr.swapAmountsForExactInput(i, j, a, limitPrice);
|
||||
}
|
||||
|
||||
/// @notice Compute fee and net amounts for a gross input (fee rounded up to favor the pool).
|
||||
/// @return feeUint fee taken (uint) and netUint remaining for protocol use (uint)
|
||||
function _computeFee(uint256 gross) internal view returns (uint256 feeUint, uint256 netUint) {
|
||||
return _computeFee(gross, SWAP_FEE_PPM);
|
||||
}
|
||||
|
||||
/// @notice Convenience: return gross = net + fee(net) using ceiling for fee.
|
||||
function _addFee(uint256 netUint) internal view returns (uint256 gross) {
|
||||
return _addFee(netUint, SWAP_FEE_PPM);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
||||
// Initialization Mint
|
||||
//
|
||||
|
||||
function initialMint(address receiver, uint256 lpTokens, int128 KAPPA) external
|
||||
function initialMint(address receiver, uint256 lpTokens, int128 KAPPA) external nonReentrant
|
||||
returns (uint256 lpMinted) {
|
||||
uint256 n = tokens.length;
|
||||
|
||||
@@ -62,7 +62,8 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
||||
// Regular Mint and Burn
|
||||
//
|
||||
|
||||
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 nonReentrant
|
||||
returns (uint256 lpMinted) {
|
||||
require(deadline == 0 || block.timestamp <= deadline, "mint: deadline exceeded");
|
||||
uint256 n = tokens.length;
|
||||
|
||||
@@ -134,7 +135,7 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
||||
/// @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
|
||||
function burn(address payer, address receiver, uint256 lpAmount, uint256 deadline) external nonReentrant
|
||||
returns (uint256[] memory withdrawAmounts) {
|
||||
require(deadline == 0 || block.timestamp <= deadline, "burn: deadline exceeded");
|
||||
uint256 n = tokens.length;
|
||||
@@ -252,6 +253,83 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
||||
// Swap-Mint and Burn-Swap
|
||||
//
|
||||
|
||||
/// @notice Calculate the amounts for a swap mint operation
|
||||
/// @dev This is a pure view function that computes swap mint amounts from provided state
|
||||
/// @param inputTokenIndex index of the input token
|
||||
/// @param maxAmountIn maximum amount of token to deposit (inclusive of fee)
|
||||
/// @param swapFeePpm fee in parts-per-million
|
||||
/// @param lmsrState current LMSR state
|
||||
/// @param bases_ scaling bases for each token
|
||||
/// @param totalSupply_ current total LP token supply
|
||||
/// @return amountInUsed actual input amount used (excluding fee)
|
||||
/// @return fee fee amount charged
|
||||
/// @return lpMinted LP tokens that would be minted
|
||||
function swapMintAmounts(
|
||||
uint256 inputTokenIndex,
|
||||
uint256 maxAmountIn,
|
||||
uint256 swapFeePpm,
|
||||
LMSRStabilized.State memory lmsrState,
|
||||
uint256[] memory bases_,
|
||||
uint256 totalSupply_
|
||||
) public pure returns (uint256 amountInUsed, uint256 fee, uint256 lpMinted) {
|
||||
require(inputTokenIndex < bases_.length, "swapMintAmounts: idx");
|
||||
require(maxAmountIn > 0, "swapMintAmounts: input zero");
|
||||
require(lmsrState.nAssets > 0, "swapMintAmounts: uninit pool");
|
||||
|
||||
// Compute fee on gross maxAmountIn to get an initial net estimate
|
||||
uint256 feeGuess = 0;
|
||||
uint256 netUintGuess = maxAmountIn;
|
||||
if (swapFeePpm > 0) {
|
||||
feeGuess = (maxAmountIn * swapFeePpm + 999999) / 1000000; // ceil fee
|
||||
netUintGuess = maxAmountIn - feeGuess;
|
||||
}
|
||||
|
||||
// Convert the net guess to internal (floor)
|
||||
int128 netInternalGuess = _uintToInternalFloorPure(netUintGuess, bases_[inputTokenIndex]);
|
||||
require(netInternalGuess > int128(0), "swapMintAmounts: input too small after fee");
|
||||
|
||||
// Use LMSR view to determine actual internal consumed and size-increase (ΔS) for mint
|
||||
(int128 amountInInternalUsed, int128 sizeIncreaseInternal) =
|
||||
LMSRStabilized.swapAmountsForMint(lmsrState.nAssets, lmsrState.kappa, lmsrState.qInternal,
|
||||
inputTokenIndex, netInternalGuess);
|
||||
|
||||
// amountInInternalUsed may be <= netInternalGuess. Convert to uint (ceil) to determine actual transfer
|
||||
amountInUsed = _internalToUintCeilPure(amountInInternalUsed, bases_[inputTokenIndex]);
|
||||
require(amountInUsed > 0, "swapMintAmounts: input zero after internal conversion");
|
||||
|
||||
// Compute fee on the actual used input (ceiling)
|
||||
fee = 0;
|
||||
if (swapFeePpm > 0) {
|
||||
fee = (amountInUsed * swapFeePpm + 999999) / 1000000; // ceil fee
|
||||
}
|
||||
uint256 totalTransfer = amountInUsed + fee;
|
||||
require(totalTransfer > 0 && totalTransfer <= maxAmountIn, "swapMintAmounts: transfer exceeds max");
|
||||
|
||||
// Compute old and new scaled size metrics to determine LP minted
|
||||
int128 oldTotal = _computeSizeMetricPure(lmsrState.qInternal);
|
||||
require(oldTotal > int128(0), "swapMintAmounts: zero total");
|
||||
uint256 oldScaled = ABDKMath64x64.mulu(oldTotal, LP_SCALE);
|
||||
|
||||
int128 newTotal = oldTotal.add(sizeIncreaseInternal);
|
||||
uint256 newScaled = ABDKMath64x64.mulu(newTotal, LP_SCALE);
|
||||
|
||||
if (totalSupply_ == 0) {
|
||||
// If somehow supply zero (shouldn't happen as lmsr.nAssets>0), mint newScaled
|
||||
lpMinted = newScaled;
|
||||
} else {
|
||||
require(oldScaled > 0, "swapMintAmounts: oldScaled zero");
|
||||
uint256 delta = (newScaled > oldScaled) ? (newScaled - oldScaled) : 0;
|
||||
if (delta > 0) {
|
||||
// floor truncation rounds in favor of pool
|
||||
lpMinted = (totalSupply_ * delta) / oldScaled;
|
||||
} else {
|
||||
lpMinted = 0;
|
||||
}
|
||||
}
|
||||
|
||||
require(lpMinted > 0, "swapMintAmounts: zero LP minted");
|
||||
}
|
||||
|
||||
/// @notice Single-token mint: deposit a single token, charge swap-LMSR cost, and mint LP.
|
||||
/// @dev swapMint executes as an exact-in planned swap followed by proportional scaling of qInternal.
|
||||
/// The function emits SwapMint (gross, net, fee) and also emits Mint for LP issuance.
|
||||
@@ -270,7 +348,7 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
||||
uint256 deadline,
|
||||
uint256 swapFeePpm,
|
||||
uint256 protocolFeePpm
|
||||
) external returns (uint256 lpMinted) {
|
||||
) external nonReentrant returns (uint256 lpMinted) {
|
||||
uint256 n = tokens.length;
|
||||
require(inputTokenIndex < n, "swapMint: idx");
|
||||
require(maxAmountIn > 0, "swapMint: input zero");
|
||||
@@ -362,83 +440,6 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
||||
return actualLpToMint;
|
||||
}
|
||||
|
||||
/// @notice Calculate the amounts for a swap mint operation
|
||||
/// @dev This is a pure view function that computes swap mint amounts from provided state
|
||||
/// @param inputTokenIndex index of the input token
|
||||
/// @param maxAmountIn maximum amount of token to deposit (inclusive of fee)
|
||||
/// @param swapFeePpm fee in parts-per-million
|
||||
/// @param lmsrState current LMSR state
|
||||
/// @param bases_ scaling bases for each token
|
||||
/// @param totalSupply_ current total LP token supply
|
||||
/// @return amountInUsed actual input amount used (excluding fee)
|
||||
/// @return fee fee amount charged
|
||||
/// @return lpMinted LP tokens that would be minted
|
||||
function swapMintAmounts(
|
||||
uint256 inputTokenIndex,
|
||||
uint256 maxAmountIn,
|
||||
uint256 swapFeePpm,
|
||||
LMSRStabilized.State memory lmsrState,
|
||||
uint256[] memory bases_,
|
||||
uint256 totalSupply_
|
||||
) public pure returns (uint256 amountInUsed, uint256 fee, uint256 lpMinted) {
|
||||
require(inputTokenIndex < bases_.length, "swapMintAmounts: idx");
|
||||
require(maxAmountIn > 0, "swapMintAmounts: input zero");
|
||||
require(lmsrState.nAssets > 0, "swapMintAmounts: uninit pool");
|
||||
|
||||
// Compute fee on gross maxAmountIn to get an initial net estimate
|
||||
uint256 feeGuess = 0;
|
||||
uint256 netUintGuess = maxAmountIn;
|
||||
if (swapFeePpm > 0) {
|
||||
feeGuess = (maxAmountIn * swapFeePpm + 999999) / 1000000; // ceil fee
|
||||
netUintGuess = maxAmountIn - feeGuess;
|
||||
}
|
||||
|
||||
// Convert the net guess to internal (floor)
|
||||
int128 netInternalGuess = _uintToInternalFloorPure(netUintGuess, bases_[inputTokenIndex]);
|
||||
require(netInternalGuess > int128(0), "swapMintAmounts: input too small after fee");
|
||||
|
||||
// Use LMSR view to determine actual internal consumed and size-increase (ΔS) for mint
|
||||
(int128 amountInInternalUsed, int128 sizeIncreaseInternal) =
|
||||
LMSRStabilized.swapAmountsForMint(lmsrState.nAssets, lmsrState.kappa, lmsrState.qInternal,
|
||||
inputTokenIndex, netInternalGuess);
|
||||
|
||||
// amountInInternalUsed may be <= netInternalGuess. Convert to uint (ceil) to determine actual transfer
|
||||
amountInUsed = _internalToUintCeilPure(amountInInternalUsed, bases_[inputTokenIndex]);
|
||||
require(amountInUsed > 0, "swapMintAmounts: input zero after internal conversion");
|
||||
|
||||
// Compute fee on the actual used input (ceiling)
|
||||
fee = 0;
|
||||
if (swapFeePpm > 0) {
|
||||
fee = (amountInUsed * swapFeePpm + 999999) / 1000000; // ceil fee
|
||||
}
|
||||
uint256 totalTransfer = amountInUsed + fee;
|
||||
require(totalTransfer > 0 && totalTransfer <= maxAmountIn, "swapMintAmounts: transfer exceeds max");
|
||||
|
||||
// Compute old and new scaled size metrics to determine LP minted
|
||||
int128 oldTotal = _computeSizeMetricPure(lmsrState.qInternal);
|
||||
require(oldTotal > int128(0), "swapMintAmounts: zero total");
|
||||
uint256 oldScaled = ABDKMath64x64.mulu(oldTotal, LP_SCALE);
|
||||
|
||||
int128 newTotal = oldTotal.add(sizeIncreaseInternal);
|
||||
uint256 newScaled = ABDKMath64x64.mulu(newTotal, LP_SCALE);
|
||||
|
||||
if (totalSupply_ == 0) {
|
||||
// If somehow supply zero (shouldn't happen as lmsr.nAssets>0), mint newScaled
|
||||
lpMinted = newScaled;
|
||||
} else {
|
||||
require(oldScaled > 0, "swapMintAmounts: oldScaled zero");
|
||||
uint256 delta = (newScaled > oldScaled) ? (newScaled - oldScaled) : 0;
|
||||
if (delta > 0) {
|
||||
// floor truncation rounds in favor of pool
|
||||
lpMinted = (totalSupply_ * delta) / oldScaled;
|
||||
} else {
|
||||
lpMinted = 0;
|
||||
}
|
||||
}
|
||||
|
||||
require(lpMinted > 0, "swapMintAmounts: zero LP minted");
|
||||
}
|
||||
|
||||
/// @notice Calculate the amounts for a burn swap operation
|
||||
/// @dev This is a pure view function that computes burn swap amounts from provided state
|
||||
/// @param lpAmount amount of LP tokens to burn
|
||||
@@ -490,7 +491,7 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
||||
uint256 deadline,
|
||||
uint256 swapFeePpm,
|
||||
uint256 protocolFeePpm
|
||||
) external returns (uint256 amountOutUint) {
|
||||
) external nonReentrant returns (uint256 amountOutUint) {
|
||||
uint256 n = tokens.length;
|
||||
require(inputTokenIndex < n, "burnSwap: idx");
|
||||
require(lpAmount > 0, "burnSwap: zero lp");
|
||||
|
||||
@@ -166,13 +166,12 @@ contract PartyPoolView is PartyPoolHelpers {
|
||||
|
||||
/**
|
||||
* @dev The fee to be charged for a given loan.
|
||||
* @param token The loan currency.
|
||||
* @param amount The amount of tokens lent.
|
||||
* @return fee The amount of `token` to be charged for the loan, on top of the returned principal.
|
||||
*/
|
||||
function flashFee(
|
||||
IPartyPool pool,
|
||||
address token,
|
||||
address /*token*/,
|
||||
uint256 amount
|
||||
) external view returns (uint256 fee) {
|
||||
(fee,) = _computeFee(amount, pool.flashFeePpm());
|
||||
|
||||
@@ -39,7 +39,7 @@ contract FlashBorrower is IERC3156FlashBorrower {
|
||||
}
|
||||
|
||||
function onFlashLoan(
|
||||
address initiator,
|
||||
address /*initiator*/,
|
||||
address token,
|
||||
uint256 amount,
|
||||
uint256 fee,
|
||||
|
||||
@@ -42,7 +42,7 @@ contract FlashBorrower is IERC3156FlashBorrower {
|
||||
}
|
||||
|
||||
function onFlashLoan(
|
||||
address initiator,
|
||||
address /*initiator*/,
|
||||
address token,
|
||||
uint256 amount,
|
||||
uint256 fee,
|
||||
|
||||
Reference in New Issue
Block a user