diff --git a/doc/introduction.md b/doc/introduction.md index 0605983..136dfb6 100644 --- a/doc/introduction.md +++ b/doc/introduction.md @@ -24,8 +24,8 @@ Naturally multi-asset, Liquidity Party altcoin pools provide direct, one-hop swa | Assets | Pairs | Swap Gas | Mint Gas | |-------:|------:|---------:|----------:| -| 2 | 1 | 132,000 | 143,000 | -| 2* | 1 | 119,000 | 143,000 | +| 2 | 1 | 131,000 | 143,000 | +| 2* | 1 | 118,000 | 143,000 | | 10 | 45 | 142,000 | 412,000 | | 20 | 190 | 157,000 | 749,000 | | 50 | 1225 | 199,000 | 1,760,000 | diff --git a/src/Deploy.sol b/src/Deploy.sol index 5a6cc19..9ccb274 100644 --- a/src/Deploy.sol +++ b/src/Deploy.sol @@ -11,6 +11,8 @@ import {PartyPoolSwapImpl} from "./PartyPoolSwapImpl.sol"; import {PartyPoolViewer} from "./PartyPoolViewer.sol"; 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) { return new PartyPlanner( @@ -18,8 +20,8 @@ library Deploy { new PartyPoolMintImpl(), new PartyPoolDeployer(), new PartyPoolBalancedPairDeployer(), - 0, // protocolFeePpm = 0 for deploy helper - address(0) // protocolFeeAddress = address(0) for deploy helper + PROTOCOL_FEE_PPM, + PROTOCOL_FEE_RECEIVER ); } @@ -33,10 +35,6 @@ library Deploy { uint256 _flashFeePpm, bool _stable ) 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 ? new PartyPoolBalancedPair( name_, @@ -46,8 +44,8 @@ library Deploy { _kappa, _swapFeePpm, _flashFeePpm, - protocolFeePpm, - protocolAddr, + PROTOCOL_FEE_PPM, + PROTOCOL_FEE_RECEIVER, new PartyPoolSwapImpl(), new PartyPoolMintImpl() ) : @@ -59,8 +57,8 @@ library Deploy { _kappa, _swapFeePpm, _flashFeePpm, - protocolFeePpm, - protocolAddr, + PROTOCOL_FEE_PPM, + PROTOCOL_FEE_RECEIVER, new PartyPoolSwapImpl(), new PartyPoolMintImpl() ); diff --git a/src/PartyPool.sol b/src/PartyPool.sol index 2cc0664..37144e1 100644 --- a/src/PartyPool.sol +++ b/src/PartyPool.sol @@ -146,8 +146,8 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool { /// @inheritdoc IPartyPool function initialMint(address receiver, uint256 lpTokens) external returns (uint256 lpMinted) { - bytes memory data = abi.encodeWithSignature( - "initialMint(address,uint256,int128)", + bytes memory data = abi.encodeWithSelector( + PartyPoolMintImpl.initialMint.selector, receiver, lpTokens, KAPPA @@ -164,8 +164,8 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool { /// @param deadline timestamp after which the transaction will revert. Pass 0 to ignore. function mint(address payer, address receiver, uint256 lpTokenAmount, uint256 deadline) external returns (uint256 lpMinted) { - bytes memory data = abi.encodeWithSignature( - "mint(address,address,uint256,uint256)", + bytes memory data = abi.encodeWithSelector( + PartyPoolMintImpl.mint.selector, payer, receiver, lpTokenAmount, @@ -183,8 +183,8 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool { /// @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) { - bytes memory data = abi.encodeWithSignature( - "burn(address,address,uint256,uint256)", + bytes memory data = abi.encodeWithSelector( + PartyPoolMintImpl.burn.selector, payer, receiver, lpAmount, @@ -226,11 +226,19 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool { (uint256 totalTransferAmount, uint256 amountOutUint, int128 amountInInternalUsed, int128 amountOutInternal, , uint256 feeUint) = _quoteSwapExactIn(inputTokenIndex, outputTokenIndex, maxAmountIn, limitPrice); - // Transfer tokens - tokens[inputTokenIndex].safeTransferFrom(payer, address(this), totalTransferAmount); - uint256 balIAfter = IERC20(tokens[inputTokenIndex]).balanceOf(address(this)); - tokens[outputTokenIndex].safeTransfer(receiver, amountOutUint); - uint256 balJAfter = IERC20(tokens[outputTokenIndex]).balanceOf(address(this)); + // Cache token references for fewer SLOADs + IERC20 tokenIn = tokens[inputTokenIndex]; + IERC20 tokenOut = tokens[outputTokenIndex]; + + // 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 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) - _recordCachedBalance(inputTokenIndex, balIAfter); - _recordCachedBalance(outputTokenIndex, balJAfter); + // Inline _recordCachedBalance: ensure onchain >= owed then set cached = onchain - owed + require(balIAfter >= protocolFeesOwed[inputTokenIndex], "balance < protocol owed"); + 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 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); } @@ -275,8 +286,6 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool { ) { 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) (, uint256 netUintForSwap) = _computeFee(maxAmountIn, SWAP_FEE_PPM); @@ -316,8 +325,8 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool { int128 limitPrice, uint256 deadline ) external returns (uint256 amountInUsed, uint256 amountOut, uint256 fee) { - bytes memory data = abi.encodeWithSignature( - 'swapToLimit(address,address,uint256,uint256,int128,uint256,uint256,uint256)', + bytes memory data = abi.encodeWithSelector( + PartyPoolSwapImpl.swapToLimit.selector, payer, receiver, inputTokenIndex, @@ -347,8 +356,8 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool { uint256 maxAmountIn, uint256 deadline ) external returns (uint256 lpMinted) { - bytes memory data = abi.encodeWithSignature( - "swapMint(address,address,uint256,uint256,uint256,uint256,uint256)", + bytes memory data = abi.encodeWithSelector( + PartyPoolMintImpl.swapMint.selector, payer, receiver, inputTokenIndex, @@ -377,8 +386,8 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool { uint256 inputTokenIndex, uint256 deadline ) external returns (uint256 amountOutUint) { - bytes memory data = abi.encodeWithSignature( - "burnSwap(address,address,uint256,uint256,uint256,uint256,uint256)", + bytes memory data = abi.encodeWithSelector( + PartyPoolMintImpl.burnSwap.selector, payer, receiver, 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(token.transferFrom(address(receiver), address(this), amount + fee)); + token.safeTransferFrom(address(receiver), address(this), amount + fee); // Update cached balance for the borrowed token 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; } diff --git a/src/PartyPoolBase.sol b/src/PartyPoolBase.sol index 6886cd6..5212707 100644 --- a/src/PartyPoolBase.sol +++ b/src/PartyPoolBase.sol @@ -78,11 +78,4 @@ abstract contract PartyPoolBase is ERC20Internal, ReentrancyGuard, PartyPoolHelp 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; - } - } diff --git a/src/PartyPoolSwapImpl.sol b/src/PartyPoolSwapImpl.sol index 38c4126..48e831f 100644 --- a/src/PartyPoolSwapImpl.sol +++ b/src/PartyPoolSwapImpl.sol @@ -87,9 +87,12 @@ contract PartyPoolSwapImpl is PartyPoolBase { } } - // Update caches to effective balances - _recordCachedBalance(inputTokenIndex, balIAfter); - _recordCachedBalance(outputTokenIndex, balJAfter); + // Update caches to effective balances (inline _recordCachedBalance) + require(balIAfter >= protocolFeesOwed[inputTokenIndex], "balance < protocol owed"); + 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 lmsr.applySwap(inputTokenIndex, outputTokenIndex, amountInInternalMax, amountOutInternal);