more gas optimization; protocol fee enabled by default in tests

This commit is contained in:
tim
2025-10-14 14:53:22 -04:00
parent dd009cb561
commit eab01554e1
5 changed files with 53 additions and 48 deletions

View File

@@ -24,8 +24,8 @@ Naturally multi-asset, Liquidity Party altcoin pools provide direct, one-hop swa
| Assets | Pairs | Swap Gas | Mint Gas | | Assets | Pairs | Swap Gas | Mint Gas |
|-------:|------:|---------:|----------:| |-------:|------:|---------:|----------:|
| 2 | 1 | 132,000 | 143,000 | | 2 | 1 | 131,000 | 143,000 |
| 2* | 1 | 119,000 | 143,000 | | 2* | 1 | 118,000 | 143,000 |
| 10 | 45 | 142,000 | 412,000 | | 10 | 45 | 142,000 | 412,000 |
| 20 | 190 | 157,000 | 749,000 | | 20 | 190 | 157,000 | 749,000 |
| 50 | 1225 | 199,000 | 1,760,000 | | 50 | 1225 | 199,000 | 1,760,000 |

View File

@@ -11,6 +11,8 @@ import {PartyPoolSwapImpl} from "./PartyPoolSwapImpl.sol";
import {PartyPoolViewer} from "./PartyPoolViewer.sol"; import {PartyPoolViewer} from "./PartyPoolViewer.sol";
library Deploy { library Deploy {
address internal constant PROTOCOL_FEE_RECEIVER = 0x70997970C51812dc3A010C7d01b50e0d17dc79C8; // dev account #1
uint256 internal constant PROTOCOL_FEE_PPM = 100_000; // 10%
function newPartyPlanner() internal returns (PartyPlanner) { function newPartyPlanner() internal returns (PartyPlanner) {
return new PartyPlanner( return new PartyPlanner(
@@ -18,8 +20,8 @@ library Deploy {
new PartyPoolMintImpl(), new PartyPoolMintImpl(),
new PartyPoolDeployer(), new PartyPoolDeployer(),
new PartyPoolBalancedPairDeployer(), new PartyPoolBalancedPairDeployer(),
0, // protocolFeePpm = 0 for deploy helper PROTOCOL_FEE_PPM,
address(0) // protocolFeeAddress = address(0) for deploy helper PROTOCOL_FEE_RECEIVER
); );
} }
@@ -33,10 +35,6 @@ library Deploy {
uint256 _flashFeePpm, uint256 _flashFeePpm,
bool _stable bool _stable
) internal returns (PartyPool) { ) internal returns (PartyPool) {
// default protocol fee/off parameters (per your instruction) - set to 0 / address(0)
uint256 protocolFeePpm = 0;
address protocolAddr = address(0);
return _stable && tokens_.length == 2 ? return _stable && tokens_.length == 2 ?
new PartyPoolBalancedPair( new PartyPoolBalancedPair(
name_, name_,
@@ -46,8 +44,8 @@ library Deploy {
_kappa, _kappa,
_swapFeePpm, _swapFeePpm,
_flashFeePpm, _flashFeePpm,
protocolFeePpm, PROTOCOL_FEE_PPM,
protocolAddr, PROTOCOL_FEE_RECEIVER,
new PartyPoolSwapImpl(), new PartyPoolSwapImpl(),
new PartyPoolMintImpl() new PartyPoolMintImpl()
) : ) :
@@ -59,8 +57,8 @@ library Deploy {
_kappa, _kappa,
_swapFeePpm, _swapFeePpm,
_flashFeePpm, _flashFeePpm,
protocolFeePpm, PROTOCOL_FEE_PPM,
protocolAddr, PROTOCOL_FEE_RECEIVER,
new PartyPoolSwapImpl(), new PartyPoolSwapImpl(),
new PartyPoolMintImpl() new PartyPoolMintImpl()
); );

View File

@@ -146,8 +146,8 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
/// @inheritdoc IPartyPool /// @inheritdoc IPartyPool
function initialMint(address receiver, uint256 lpTokens) external function initialMint(address receiver, uint256 lpTokens) external
returns (uint256 lpMinted) { returns (uint256 lpMinted) {
bytes memory data = abi.encodeWithSignature( bytes memory data = abi.encodeWithSelector(
"initialMint(address,uint256,int128)", PartyPoolMintImpl.initialMint.selector,
receiver, receiver,
lpTokens, lpTokens,
KAPPA KAPPA
@@ -164,8 +164,8 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
/// @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
returns (uint256 lpMinted) { returns (uint256 lpMinted) {
bytes memory data = abi.encodeWithSignature( bytes memory data = abi.encodeWithSelector(
"mint(address,address,uint256,uint256)", PartyPoolMintImpl.mint.selector,
payer, payer,
receiver, receiver,
lpTokenAmount, lpTokenAmount,
@@ -183,8 +183,8 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
/// @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 function burn(address payer, address receiver, uint256 lpAmount, uint256 deadline) external
returns (uint256[] memory withdrawAmounts) { returns (uint256[] memory withdrawAmounts) {
bytes memory data = abi.encodeWithSignature( bytes memory data = abi.encodeWithSelector(
"burn(address,address,uint256,uint256)", PartyPoolMintImpl.burn.selector,
payer, payer,
receiver, receiver,
lpAmount, lpAmount,
@@ -226,11 +226,19 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
(uint256 totalTransferAmount, uint256 amountOutUint, int128 amountInInternalUsed, int128 amountOutInternal, , uint256 feeUint) = (uint256 totalTransferAmount, uint256 amountOutUint, int128 amountInInternalUsed, int128 amountOutInternal, , uint256 feeUint) =
_quoteSwapExactIn(inputTokenIndex, outputTokenIndex, maxAmountIn, limitPrice); _quoteSwapExactIn(inputTokenIndex, outputTokenIndex, maxAmountIn, limitPrice);
// Transfer tokens // Cache token references for fewer SLOADs
tokens[inputTokenIndex].safeTransferFrom(payer, address(this), totalTransferAmount); IERC20 tokenIn = tokens[inputTokenIndex];
uint256 balIAfter = IERC20(tokens[inputTokenIndex]).balanceOf(address(this)); IERC20 tokenOut = tokens[outputTokenIndex];
tokens[outputTokenIndex].safeTransfer(receiver, amountOutUint);
uint256 balJAfter = IERC20(tokens[outputTokenIndex]).balanceOf(address(this)); // Transfer tokens in
tokenIn.safeTransferFrom(payer, address(this), totalTransferAmount);
// Compute on-chain balances as: onchain = cached + owed (+/- transfer)
uint256 balIAfter = cachedUintBalances[inputTokenIndex] + protocolFeesOwed[inputTokenIndex] + totalTransferAmount;
uint256 balJAfter = cachedUintBalances[outputTokenIndex] + protocolFeesOwed[outputTokenIndex] - amountOutUint;
// Transfer output to receiver
tokenOut.safeTransfer(receiver, amountOutUint);
// 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) {
@@ -240,14 +248,17 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
} }
} }
// Update cached uint balances for i and j using effective balances (onchain - owed) // Inline _recordCachedBalance: ensure onchain >= owed then set cached = onchain - owed
_recordCachedBalance(inputTokenIndex, balIAfter); require(balIAfter >= protocolFeesOwed[inputTokenIndex], "balance < protocol owed");
_recordCachedBalance(outputTokenIndex, balJAfter); cachedUintBalances[inputTokenIndex] = balIAfter - protocolFeesOwed[inputTokenIndex];
require(balJAfter >= protocolFeesOwed[outputTokenIndex], "balance < protocol owed");
cachedUintBalances[outputTokenIndex] = balJAfter - protocolFeesOwed[outputTokenIndex];
// Apply swap to LMSR state with the internal amounts actually used // Apply swap to LMSR state with the internal amounts actually used
lmsr.applySwap(inputTokenIndex, outputTokenIndex, amountInInternalUsed, amountOutInternal); lmsr.applySwap(inputTokenIndex, outputTokenIndex, amountInInternalUsed, amountOutInternal);
emit Swap(payer, receiver, tokens[inputTokenIndex], tokens[outputTokenIndex], totalTransferAmount, amountOutUint); emit Swap(payer, receiver, tokenIn, tokenOut, totalTransferAmount, amountOutUint);
return (totalTransferAmount, amountOutUint, feeUint); return (totalTransferAmount, amountOutUint, feeUint);
} }
@@ -275,8 +286,6 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
) )
{ {
uint256 n = tokens.length; uint256 n = tokens.length;
require(inputTokenIndex < n && outputTokenIndex < n, "swap: idx");
require(maxAmountIn > 0, "swap: input zero");
// Estimate max net input (fee on gross rounded up, then subtract) // Estimate max net input (fee on gross rounded up, then subtract)
(, uint256 netUintForSwap) = _computeFee(maxAmountIn, SWAP_FEE_PPM); (, uint256 netUintForSwap) = _computeFee(maxAmountIn, SWAP_FEE_PPM);
@@ -316,8 +325,8 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
int128 limitPrice, int128 limitPrice,
uint256 deadline uint256 deadline
) external returns (uint256 amountInUsed, uint256 amountOut, uint256 fee) { ) external returns (uint256 amountInUsed, uint256 amountOut, uint256 fee) {
bytes memory data = abi.encodeWithSignature( bytes memory data = abi.encodeWithSelector(
'swapToLimit(address,address,uint256,uint256,int128,uint256,uint256,uint256)', PartyPoolSwapImpl.swapToLimit.selector,
payer, payer,
receiver, receiver,
inputTokenIndex, inputTokenIndex,
@@ -347,8 +356,8 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
uint256 maxAmountIn, uint256 maxAmountIn,
uint256 deadline uint256 deadline
) external returns (uint256 lpMinted) { ) external returns (uint256 lpMinted) {
bytes memory data = abi.encodeWithSignature( bytes memory data = abi.encodeWithSelector(
"swapMint(address,address,uint256,uint256,uint256,uint256,uint256)", PartyPoolMintImpl.swapMint.selector,
payer, payer,
receiver, receiver,
inputTokenIndex, inputTokenIndex,
@@ -377,8 +386,8 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
uint256 inputTokenIndex, uint256 inputTokenIndex,
uint256 deadline uint256 deadline
) external returns (uint256 amountOutUint) { ) external returns (uint256 amountOutUint) {
bytes memory data = abi.encodeWithSignature( bytes memory data = abi.encodeWithSelector(
"burnSwap(address,address,uint256,uint256,uint256,uint256,uint256)", PartyPoolMintImpl.burnSwap.selector,
payer, payer,
receiver, receiver,
lpAmount, lpAmount,
@@ -422,13 +431,15 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
} }
} }
require(token.transfer(address(receiver), amount)); token.safeTransfer(address(receiver), amount);
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);
require(token.transferFrom(address(receiver), address(this), amount + fee)); token.safeTransferFrom(address(receiver), address(this), amount + fee);
// Update cached balance for the borrowed token // Update cached balance for the borrowed token
uint256 balAfter = token.balanceOf(address(this)); uint256 balAfter = token.balanceOf(address(this));
_recordCachedBalance(tokenIndex, balAfter); // Inline _recordCachedBalance logic
require(balAfter >= protocolFeesOwed[tokenIndex], "balance < protocol owed");
cachedUintBalances[tokenIndex] = balAfter - protocolFeesOwed[tokenIndex];
return true; return true;
} }

View File

@@ -78,11 +78,4 @@ abstract contract PartyPoolBase is ERC20Internal, ReentrancyGuard, PartyPoolHelp
return floorValue; return floorValue;
} }
/// @dev Helper to record cached balances as effectiveBalance = onchain - owed. Reverts if owed > onchain.
function _recordCachedBalance(uint256 idx, uint256 onchainBal) internal {
uint256 owed = protocolFeesOwed[idx];
require(onchainBal >= owed, "balance < protocol owed");
cachedUintBalances[idx] = onchainBal - owed;
}
} }

View File

@@ -87,9 +87,12 @@ contract PartyPoolSwapImpl is PartyPoolBase {
} }
} }
// Update caches to effective balances // Update caches to effective balances (inline _recordCachedBalance)
_recordCachedBalance(inputTokenIndex, balIAfter); require(balIAfter >= protocolFeesOwed[inputTokenIndex], "balance < protocol owed");
_recordCachedBalance(outputTokenIndex, balJAfter); cachedUintBalances[inputTokenIndex] = balIAfter - protocolFeesOwed[inputTokenIndex];
require(balJAfter >= protocolFeesOwed[outputTokenIndex], "balance < protocol owed");
cachedUintBalances[outputTokenIndex] = balJAfter - protocolFeesOwed[outputTokenIndex];
// Apply swap to LMSR state with the internal amounts // Apply swap to LMSR state with the internal amounts
lmsr.applySwap(inputTokenIndex, outputTokenIndex, amountInInternalMax, amountOutInternal); lmsr.applySwap(inputTokenIndex, outputTokenIndex, amountInInternalMax, amountOutInternal);