CREATE2 callback validation; init code storage contracts

This commit is contained in:
tim
2025-11-13 16:41:52 -04:00
parent c2ac0e3624
commit 9273430f2a
28 changed files with 779 additions and 588 deletions

View File

@@ -17,6 +17,7 @@ import {OwnableInternal} from "./OwnableInternal.sol";
import {PartyPoolBase} from "./PartyPoolBase.sol";
import {PartyPoolMintImpl} from "./PartyPoolMintImpl.sol";
import {PartyPoolSwapImpl} from "./PartyPoolSwapImpl.sol";
import {IPartyPoolDeployer} from "./IPartyPoolDeployer.sol";
/// @title PartyPool - LMSR-backed multi-asset pool with LP ERC20 token
/// @notice A multi-asset liquidity pool backed by the LMSRStabilized pricing model.
@@ -43,7 +44,7 @@ contract PartyPool is PartyPoolBase, OwnableExternal, ERC20External, IPartyPool
/// @notice If true, the vault has been disabled by the owner and only burns (withdrawals) are allowed.
function killed() external view returns (bool) { return _killed; }
function wrapperToken() external view returns (NativeWrapper) { return WRAPPER_TOKEN; }
function wrapperToken() external view returns (NativeWrapper) { return WRAPPER; }
/// @notice Liquidity parameter κ (Q64.64) used by the LMSR kernel: b = κ * S(q)
/// @dev Pool is constructed with a fixed κ. Clients that previously passed tradeFrac/targetSlippage
@@ -80,7 +81,7 @@ contract PartyPool is PartyPoolBase, OwnableExternal, ERC20External, IPartyPool
function swapMintImpl() external view returns (PartyPoolSwapImpl) { return SWAP_IMPL; }
/// @inheritdoc IPartyPool
function getToken(uint256 i) external view returns (IERC20) { return _tokens[i]; }
function token(uint256 i) external view returns (IERC20) { return _tokens[i]; }
/// @inheritdoc IPartyPool
function numTokens() external view returns (uint256) { return _tokens.length; }
@@ -94,61 +95,42 @@ contract PartyPool is PartyPoolBase, OwnableExternal, ERC20External, IPartyPool
/// @inheritdoc IPartyPool
function LMSR() external view returns (LMSRStabilized.State memory) { return _lmsr; }
/// @param owner_ Admin account that can disable the vault using kill()
/// @param name_ LP token name
/// @param symbol_ LP token symbol
/// @param tokens_ token addresses (n)
/// @param kappa_ liquidity parameter κ (Q64.64) used to derive b = κ * S(q)
/// @param fees_ per-asset swap fees in ppm (length must equal tokens_.length)
/// @param flashFeePpm_ fee in parts-per-million, taken for flash loans
/// @param swapImpl_ address of the SwapMint implementation contract
/// @param mintImpl_ address of the Mint implementation contract
constructor(
address owner_,
string memory name_,
string memory symbol_,
IERC20[] memory tokens_,
int128 kappa_,
uint256[] memory fees_,
uint256 flashFeePpm_,
uint256 protocolFeePpm_,
address protocolFeeAddress_,
NativeWrapper wrapperToken_,
PartyPoolSwapImpl swapImpl_,
PartyPoolMintImpl mintImpl_
)
PartyPoolBase(wrapperToken_)
OwnableExternal(owner_)
ERC20External(name_, symbol_)
constructor()
{
require(owner_ != address(0));
require(tokens_.length > 1, "Pool: need >1 asset");
_tokens = tokens_;
KAPPA = kappa_;
require(fees_.length == tokens_.length, "Pool: fees length");
// validate ppm bounds and assign
_fees = new uint256[](fees_.length);
for (uint256 i = 0; i < fees_.length; i++) {
// Cap all fees at 1%
require(fees_[i] < 10_000, "Pool: fee >= 1%");
_fees[i] = fees_[i];
}
require(flashFeePpm_ < 10_000, "Pool: flash fee >= 1%");
FLASH_FEE_PPM = flashFeePpm_;
require(protocolFeePpm_ < 400_000, "Pool: protocol fee >= 40%");
// If the protocolFeePpm_ is set, then also require the fee address to be nonzero
require(protocolFeePpm_ == 0 || protocolFeeAddress_ != address(0));
PROTOCOL_FEE_PPM = protocolFeePpm_;
protocolFeeAddress = protocolFeeAddress_;
SWAP_IMPL = swapImpl_;
MINT_IMPL = mintImpl_;
IPartyPoolDeployer.DeployParams memory p = IPartyPoolDeployer(msg.sender).params();
uint256 n = p.tokens.length;
require(n > 1, "Pool: need >1 asset");
uint256 n = tokens_.length;
_nonce = p.nonce;
WRAPPER = p.wrapper;
_name = p.name;
_symbol = p.symbol;
ownableConstructor(p.owner);
_tokens = p.tokens;
KAPPA = p.kappa;
require(p.fees.length == p.tokens.length, "Pool: fees length");
// validate ppm bounds and assign
_fees = new uint256[](p.fees.length);
for (uint256 i = 0; i < p.fees.length; i++) {
// Cap all fees at 1%
require(p.fees[i] < 10_000, "Pool: fee >= 1%");
_fees[i] = p.fees[i];
}
require(p.flashFeePpm < 10_000, "Pool: flash fee >= 1%");
FLASH_FEE_PPM = p.flashFeePpm;
require(p.protocolFeePpm < 400_000, "Pool: protocol fee >= 40%");
// If the p.protocolFeePpm is set, then also require the fee address to be nonzero
require(p.protocolFeePpm == 0 || p.protocolFeeAddress != address(0));
PROTOCOL_FEE_PPM = p.protocolFeePpm;
protocolFeeAddress = p.protocolFeeAddress;
SWAP_IMPL = p.swapImpl;
MINT_IMPL = p.mintImpl;
// Initialize token address to index mapping
for (uint i = 0; i < n;) {
_tokenAddressToIndexPlusOne[tokens_[i]] = i + 1;
_tokenAddressToIndexPlusOne[p.tokens[i]] = i + 1;
unchecked {i++;}
}
@@ -252,7 +234,8 @@ contract PartyPool is PartyPoolBase, OwnableExternal, ERC20External, IPartyPool
uint256 maxAmountIn,
int128 limitPrice,
uint256 deadline,
bool unwrap
bool unwrap,
bytes memory cbData
) external payable native nonReentrant killable returns (uint256 amountIn, uint256 amountOut, uint256 inFee) {
require(deadline == 0 || block.timestamp <= deadline, "swap: deadline exceeded");
@@ -264,24 +247,7 @@ contract PartyPool is PartyPoolBase, OwnableExternal, ERC20External, IPartyPool
IERC20 tokenIn = _tokens[inputTokenIndex];
IERC20 tokenOut = _tokens[outputTokenIndex];
if (fundingSelector == Funding.APPROVALS)
// Regular ERC20 permit of the pool to move the tokens
_receiveTokenFrom(payer, tokenIn, totalTransferAmount);
else if (fundingSelector == Funding.PREFUNDING) {
require(limitPrice==0, 'Prefunding cannot be used with a limit price');
uint256 balance = tokenIn.balanceOf(address(this));
uint256 prevBalance = _cachedUintBalances[inputTokenIndex] + _protocolFeesOwed[inputTokenIndex];
require( balance - prevBalance == totalTransferAmount, 'Incorrect prefunding amount');
}
else {
// Callback-style funding mechanism
uint256 startingBalance = tokenIn.balanceOf(address(this));
bytes memory data = abi.encodeWithSelector(fundingSelector, tokenIn, totalTransferAmount);
// Invoke the payer callback; no return value expected (reverts on failure)
Address.functionCall(payer, data);
uint256 endingBalance = tokenIn.balanceOf(address(this));
require(endingBalance-startingBalance == totalTransferAmount, 'Insufficient funds');
}
_receiveTokenFrom(payer, fundingSelector, inputTokenIndex, tokenIn, totalTransferAmount, limitPrice, cbData);
// Compute on-chain balances as: onchain = cached + owed (+/- transfer)
uint256 balIAfter = _cachedUintBalances[inputTokenIndex] + _protocolFeesOwed[inputTokenIndex] + totalTransferAmount;
@@ -369,22 +335,26 @@ contract PartyPool is PartyPoolBase, OwnableExternal, ERC20External, IPartyPool
/// @inheritdoc IPartyPool
function swapToLimit(
address payer,
bytes4 fundingSelector,
address receiver,
uint256 inputTokenIndex,
uint256 outputTokenIndex,
int128 limitPrice,
uint256 deadline,
bool unwrap
bool unwrap,
bytes memory cbData
) external payable returns (uint256 amountInUsed, uint256 amountOut, uint256 inFee) {
bytes memory data = abi.encodeWithSelector(
PartyPoolSwapImpl.swapToLimit.selector,
payer,
fundingSelector,
receiver,
inputTokenIndex,
outputTokenIndex,
limitPrice,
deadline,
unwrap,
cbData,
_pairFeePpm(inputTokenIndex, outputTokenIndex),
PROTOCOL_FEE_PPM
);