ownership & killable
This commit is contained in:
@@ -35,6 +35,7 @@ contract DeploySepolia is Script {
|
||||
|
||||
// deploy a PartyPlanner factory and create the pool via factory
|
||||
PartyPlanner planner = new PartyPlanner(
|
||||
msg.sender, // admin address is the same as the deployer
|
||||
WETH,
|
||||
swapImpl,
|
||||
mintImpl,
|
||||
|
||||
25
src/IOwnable.sol
Normal file
25
src/IOwnable.sol
Normal file
@@ -0,0 +1,25 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
|
||||
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
/**
|
||||
* @dev OpenZeppelin's Ownable contract, split into internal and external parts.
|
||||
*/
|
||||
interface IOwnable {
|
||||
/**
|
||||
* @dev The caller account is not authorized to perform an operation.
|
||||
*/
|
||||
error OwnableUnauthorizedAccount(address account);
|
||||
|
||||
/**
|
||||
* @dev The owner is not a valid owner account. (eg. `address(0)`)
|
||||
*/
|
||||
error OwnableInvalidOwner(address owner);
|
||||
|
||||
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
|
||||
|
||||
function owner() external view returns (address);
|
||||
function renounceOwnership() external;
|
||||
function transferOwnership(address newOwner) external;
|
||||
}
|
||||
@@ -8,38 +8,38 @@ import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
|
||||
/// @title IPartyPlanner
|
||||
/// @notice Interface for factory contract for creating and tracking PartyPool instances
|
||||
interface IPartyPlanner {
|
||||
interface IPartyPlanner is IOwnable {
|
||||
// Event emitted when a new pool is created
|
||||
event PartyStarted(IPartyPool indexed pool, string name, string symbol, IERC20[] tokens);
|
||||
|
||||
/// @notice Creates a new PartyPool instance and initializes it with initial deposits (legacy signature).
|
||||
/// @dev Deprecated in favour of the kappa-based overload below; kept for backwards compatibility.
|
||||
/// @param name_ LP token name
|
||||
/// @param symbol_ LP token symbol
|
||||
/// @param _tokens token addresses (n)
|
||||
/// @param _bases scaling _bases for each token (n) - used when converting to/from internal 64.64 amounts
|
||||
/// @param _tradeFrac trade fraction in 64.64 fixed-point (as used by LMSR)
|
||||
/// @param _targetSlippage target slippage in 64.64 fixed-point (as used by LMSR)
|
||||
/// @param _swapFeePpm fee in parts-per-million, taken from swap input amounts before LMSR calculations
|
||||
/// @param _flashFeePpm fee in parts-per-million, taken for flash loans
|
||||
/// @param _stable if true and assets.length==2, then the optimization for 2-asset stablecoin pools is activated
|
||||
/// @param name LP token name
|
||||
/// @param symbol LP token symbol
|
||||
/// @param tokens token addresses (n)
|
||||
/// @param bases scaling bases for each token (n) - used when converting to/from internal 64.64 amounts
|
||||
/// @param tradeFrac trade fraction in 64.64 fixed-point (as used by LMSR)
|
||||
/// @param targetSlippage target slippage in 64.64 fixed-point (as used by LMSR)
|
||||
/// @param swapFeePpm fee in parts-per-million, taken from swap input amounts before LMSR calculations
|
||||
/// @param flashFeePpm fee in parts-per-million, taken for flash loans
|
||||
/// @param stable if true and assets.length==2, then the optimization for 2-asset stablecoin pools is activated
|
||||
/// @param payer address that provides the initial token deposits
|
||||
/// @param receiver address that receives the minted LP _tokens
|
||||
/// @param receiver address that receives the minted LP tokens
|
||||
/// @param initialDeposits amounts of each token to deposit initially
|
||||
/// @param deadline Reverts if nonzero and the current blocktime is later than the deadline
|
||||
/// @return pool Address of the newly created and initialized PartyPool
|
||||
/// @return lpAmount Amount of LP _tokens minted to the receiver
|
||||
/// @return lpAmount Amount of LP tokens minted to the receiver
|
||||
function newPool(
|
||||
// Pool constructor args (legacy)
|
||||
string memory name_,
|
||||
string memory symbol_,
|
||||
IERC20[] memory _tokens,
|
||||
uint256[] memory _bases,
|
||||
int128 _tradeFrac,
|
||||
int128 _targetSlippage,
|
||||
uint256 _swapFeePpm,
|
||||
uint256 _flashFeePpm,
|
||||
bool _stable,
|
||||
string memory name,
|
||||
string memory symbol,
|
||||
IERC20[] memory tokens,
|
||||
uint256[] memory bases,
|
||||
int128 tradeFrac,
|
||||
int128 targetSlippage,
|
||||
uint256 swapFeePpm,
|
||||
uint256 flashFeePpm,
|
||||
bool stable,
|
||||
// Initial deposit information
|
||||
address payer,
|
||||
address receiver,
|
||||
@@ -49,30 +49,30 @@ interface IPartyPlanner {
|
||||
) external returns (IPartyPool pool, uint256 lpAmount);
|
||||
|
||||
/// @notice Creates a new PartyPool instance and initializes it with initial deposits (kappa-based).
|
||||
/// @param name_ LP token name
|
||||
/// @param symbol_ LP token symbol
|
||||
/// @param _tokens token addresses (n)
|
||||
/// @param _bases scaling _bases for each token (n) - used when converting to/from internal 64.64 amounts
|
||||
/// @param _kappa liquidity parameter κ in 64.64 fixed-point used to derive b = κ * S(q)
|
||||
/// @param _swapFeePpm fee in parts-per-million, taken from swap input amounts before LMSR calculations
|
||||
/// @param _flashFeePpm fee in parts-per-million, taken for flash loans
|
||||
/// @param _stable if true and assets.length==2, then the optimization for 2-asset stablecoin pools is activated
|
||||
/// @param name LP token name
|
||||
/// @param symbol LP token symbol
|
||||
/// @param tokens token addresses (n)
|
||||
/// @param bases scaling bases for each token (n) - used when converting to/from internal 64.64 amounts
|
||||
/// @param kappa liquidity parameter κ in 64.64 fixed-point used to derive b = κ * S(q)
|
||||
/// @param swapFeePpm fee in parts-per-million, taken from swap input amounts before LMSR calculations
|
||||
/// @param flashFeePpm fee in parts-per-million, taken for flash loans
|
||||
/// @param stable if true and assets.length==2, then the optimization for 2-asset stablecoin pools is activated
|
||||
/// @param payer address that provides the initial token deposits
|
||||
/// @param receiver address that receives the minted LP _tokens
|
||||
/// @param receiver address that receives the minted LP tokens
|
||||
/// @param initialDeposits amounts of each token to deposit initially
|
||||
/// @param deadline Reverts if nonzero and the current blocktime is later than the deadline
|
||||
/// @return pool Address of the newly created and initialized PartyPool
|
||||
/// @return lpAmount Amount of LP _tokens minted to the receiver
|
||||
/// @return lpAmount Amount of LP tokens minted to the receiver
|
||||
function newPool(
|
||||
// Pool constructor args (kappa-based)
|
||||
string memory name_,
|
||||
string memory symbol_,
|
||||
IERC20[] memory _tokens,
|
||||
uint256[] memory _bases,
|
||||
int128 _kappa,
|
||||
uint256 _swapFeePpm,
|
||||
uint256 _flashFeePpm,
|
||||
bool _stable,
|
||||
string memory name,
|
||||
string memory symbol,
|
||||
IERC20[] memory tokens,
|
||||
uint256[] memory bases,
|
||||
int128 kappa,
|
||||
uint256 swapFeePpm,
|
||||
uint256 flashFeePpm,
|
||||
bool stable,
|
||||
// Initial deposit information
|
||||
address payer,
|
||||
address receiver,
|
||||
@@ -96,8 +96,8 @@ interface IPartyPlanner {
|
||||
/// @return pools Array of pool addresses for the requested page
|
||||
function getAllPools(uint256 offset, uint256 limit) external view returns (IPartyPool[] memory pools);
|
||||
|
||||
/// @notice Returns the total number of unique _tokens
|
||||
/// @return The total count of unique _tokens
|
||||
/// @notice Returns the total number of unique tokens
|
||||
/// @return The total count of unique tokens
|
||||
function tokenCount() external view returns (uint256);
|
||||
|
||||
/// @notice Retrieves a page of token addresses
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
pragma solidity ^0.8.30;
|
||||
|
||||
import "../lib/openzeppelin-contracts/contracts/interfaces/IERC3156FlashBorrower.sol";
|
||||
import "./NativeWrapper.sol";
|
||||
import "./IOwnable.sol";
|
||||
import "./LMSRStabilized.sol";
|
||||
import "./NativeWrapper.sol";
|
||||
import {IERC20Metadata} from "../lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol";
|
||||
import {IERC20} from "../lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
|
||||
|
||||
@@ -20,7 +21,7 @@ import {IERC20} from "../lib/openzeppelin-contracts/contracts/token/ERC20/IERC20
|
||||
/// representation used by the LMSR library. Cached on-chain uint balances are kept to reduce balanceOf calls.
|
||||
/// The contract uses ceiling/floor rules described in function comments to bias rounding in favor of the pool
|
||||
/// (i.e., floor outputs to users, ceil inputs/fees where appropriate).
|
||||
interface IPartyPool is IERC20Metadata {
|
||||
interface IPartyPool is IERC20Metadata, IOwnable {
|
||||
// All int128's are ABDKMath64x64 format
|
||||
|
||||
// Events
|
||||
@@ -62,10 +63,10 @@ interface IPartyPool is IERC20Metadata {
|
||||
function LMSR() external view returns (LMSRStabilized.State memory);
|
||||
|
||||
/// @notice Token addresses comprising the pool. Effectively immutable after construction.
|
||||
/// @dev _tokens[i] corresponds to the i-th asset and maps to index i in the internal LMSR arrays.
|
||||
/// @dev tokens[i] corresponds to the i-th asset and maps to index i in the internal LMSR arrays.
|
||||
function getToken(uint256) external view returns (IERC20); // get single token
|
||||
|
||||
/// @notice Returns the number of _tokens (n) in the pool.
|
||||
/// @notice Returns the number of tokens (n) in the pool.
|
||||
function numTokens() external view returns (uint256);
|
||||
|
||||
/// @notice Returns the list of all token addresses in the pool (copy).
|
||||
@@ -75,7 +76,7 @@ interface IPartyPool is IERC20Metadata {
|
||||
function wrapperToken() external view returns (NativeWrapper);
|
||||
|
||||
/// @notice Per-token uint base denominators used to convert uint token amounts <-> internal Q64.64 representation.
|
||||
/// @dev denominators()[i] is the base for _tokens[i]. These _bases are chosen by deployer and must match token decimals.
|
||||
/// @dev denominators()[i] is the base for tokens[i]. These bases are chosen by deployer and must match token decimals.
|
||||
function denominators() external view returns (uint256[] memory);
|
||||
|
||||
/// @notice Per-swap fee in parts-per-million (ppm). Fee is taken from input amounts before LMSR computations.
|
||||
@@ -88,10 +89,10 @@ interface IPartyPool is IERC20Metadata {
|
||||
/// @dev This is the fraction (in ppm) of the pool-collected fees that are owed to the protocol.
|
||||
function protocolFeePpm() external view returns (uint256);
|
||||
|
||||
/// @notice Address that will receive collected protocol _tokens when collectProtocolFees() is called.
|
||||
/// @notice Address that will receive collected protocol tokens when collectProtocolFees() is called.
|
||||
function protocolFeeAddress() external view returns (address);
|
||||
|
||||
/// @notice Protocol fee ledger accessor. Returns _tokens owed (raw uint token units) from this pool as protocol fees
|
||||
/// @notice Protocol fee ledger accessor. Returns tokens owed (raw uint token units) from this pool as protocol fees
|
||||
/// that have not yet been transferred out.
|
||||
function allProtocolFeesOwed() external view returns (uint256[] memory);
|
||||
|
||||
@@ -102,31 +103,36 @@ interface IPartyPool is IERC20Metadata {
|
||||
/// @dev Pools are constructed with a κ value; this getter exposes the κ used by the pool.
|
||||
function kappa() external view returns (int128);
|
||||
|
||||
/// @notice If a security problem is found, the vault owner may call this function to permanently disable swap and
|
||||
/// mint functionality, leaving only burns (withdrawals) working.
|
||||
function kill() external;
|
||||
function killed() external view returns (bool);
|
||||
|
||||
// Initialization / Mint / Burn (LP token managed)
|
||||
|
||||
/// @notice Initial mint to set up pool for the first time.
|
||||
/// @dev Assumes _tokens have already been transferred to the pool prior to calling.
|
||||
/// @dev Assumes tokens have already been transferred to the pool prior to calling.
|
||||
/// Can only be called when the pool is uninitialized (totalSupply() == 0 or _lmsr.nAssets == 0).
|
||||
/// @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 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
|
||||
function initialMint(address receiver, uint256 lpTokens) external payable returns (uint256 lpMinted);
|
||||
|
||||
/// @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.
|
||||
/// - For subsequent mints: payer must approve the required token amounts before calling.
|
||||
/// Rounds follow the pool-favorable conventions documented in helpers (ceil inputs, floor outputs).
|
||||
/// @param payer address that provides the input _tokens (ignored for initial deposit)
|
||||
/// @param receiver address that receives the LP _tokens
|
||||
/// @param lpTokenAmount desired amount of LP _tokens to mint (ignored for initial deposit)
|
||||
/// @param payer address that provides the input tokens (ignored for initial deposit)
|
||||
/// @param receiver address that receives the LP tokens
|
||||
/// @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.
|
||||
/// @return lpMinted the actual amount of lpToken minted
|
||||
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 This function forwards the call to the burn implementation via delegatecall
|
||||
/// @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 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.
|
||||
/// @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);
|
||||
@@ -149,9 +155,9 @@ interface IPartyPool is IERC20Metadata {
|
||||
|
||||
/// @notice Swap input token inputTokenIndex -> token outputTokenIndex. Payer must approve token inputTokenIndex.
|
||||
/// @dev This function transfers the exact gross input (including fee) from payer and sends the computed output to receiver.
|
||||
/// Non-standard _tokens (fee-on-transfer, rebasers) are rejected via balance checks.
|
||||
/// Non-standard tokens (fee-on-transfer, rebasers) are rejected via balance checks.
|
||||
/// @param payer address of the account that pays for the swap
|
||||
/// @param receiver address that will receive the output _tokens
|
||||
/// @param receiver address that will receive the output tokens
|
||||
/// @param inputTokenIndex index of input asset
|
||||
/// @param outputTokenIndex index of output asset
|
||||
/// @param maxAmountIn maximum amount of token inputTokenIndex (uint256) to transfer in (inclusive of fees)
|
||||
@@ -173,7 +179,7 @@ interface IPartyPool is IERC20Metadata {
|
||||
/// @dev If balances prevent fully reaching the limit, the function caps and returns actuals.
|
||||
/// The payer must transfer the exact gross input computed by the view.
|
||||
/// @param payer address of the account that pays for the swap
|
||||
/// @param receiver address that will receive the output _tokens
|
||||
/// @param receiver address that will receive the output tokens
|
||||
/// @param inputTokenIndex index of input asset
|
||||
/// @param outputTokenIndex index of output asset
|
||||
/// @param limitPrice target marginal price to reach (must be > 0)
|
||||
@@ -193,7 +199,7 @@ interface IPartyPool is IERC20Metadata {
|
||||
/// @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.
|
||||
/// @param payer who transfers the input token
|
||||
/// @param receiver who receives the minted LP _tokens
|
||||
/// @param receiver who receives the minted LP tokens
|
||||
/// @param inputTokenIndex index of the input token
|
||||
/// @param maxAmountIn maximum uint token input (inclusive of fee)
|
||||
/// @param deadline optional deadline
|
||||
@@ -206,11 +212,11 @@ interface IPartyPool is IERC20Metadata {
|
||||
uint256 deadline
|
||||
) external payable returns (uint256 lpMinted);
|
||||
|
||||
/// @notice Burn LP _tokens then swap the redeemed proportional basket into a single asset `inputTokenIndex` and send to receiver.
|
||||
/// @dev The function burns LP _tokens (authorization via allowance if needed), sends the single-asset payout and updates LMSR state.
|
||||
/// @param payer who burns LP _tokens
|
||||
/// @notice Burn LP tokens then swap the redeemed proportional basket into a single asset `inputTokenIndex` and send to receiver.
|
||||
/// @dev The function burns LP tokens (authorization via allowance if needed), sends the single-asset payout and updates LMSR state.
|
||||
/// @param payer who burns LP tokens
|
||||
/// @param receiver who receives the single asset
|
||||
/// @param lpAmount amount of LP _tokens to burn
|
||||
/// @param lpAmount amount of LP tokens to burn
|
||||
/// @param inputTokenIndex index of target asset to receive
|
||||
/// @param deadline optional deadline
|
||||
/// @return amountOutUint uint amount of asset inputTokenIndex sent to receiver
|
||||
@@ -225,9 +231,9 @@ interface IPartyPool is IERC20Metadata {
|
||||
|
||||
/**
|
||||
* @dev Initiate a flash loan.
|
||||
* @param receiver The receiver of the _tokens in the loan, and the receiver of the callback.
|
||||
* @param receiver The receiver of the tokens in the loan, and the receiver of the callback.
|
||||
* @param token The loan currency.
|
||||
* @param amount The amount of _tokens lent.
|
||||
* @param amount The amount of tokens lent.
|
||||
* @param data Arbitrary data structure, intended to contain user-defined parameters.
|
||||
*/
|
||||
function flashLoan(
|
||||
|
||||
62
src/OwnableExternal.sol
Normal file
62
src/OwnableExternal.sol
Normal file
@@ -0,0 +1,62 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
|
||||
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "../lib/openzeppelin-contracts/contracts/utils/Context.sol";
|
||||
import {OwnableInternal} from "./OwnableInternal.sol";
|
||||
import {IOwnable} from "./IOwnable.sol";
|
||||
|
||||
/**
|
||||
* @dev Contract module which provides a basic access control mechanism, where
|
||||
* there is an account (an owner) that can be granted exclusive access to
|
||||
* specific functions.
|
||||
*
|
||||
* The initial owner is set to the address provided by the deployer. This can
|
||||
* later be changed with {transferOwnership}.
|
||||
*
|
||||
* This module is used through inheritance. It will make available the modifier
|
||||
* `onlyOwner`, which can be applied to your functions to restrict their use to
|
||||
* the owner.
|
||||
*/
|
||||
abstract contract OwnableExternal is OwnableInternal, IOwnable {
|
||||
/**
|
||||
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
|
||||
*/
|
||||
constructor(address initialOwner) {
|
||||
if (initialOwner == address(0)) {
|
||||
revert OwnableInvalidOwner(address(0));
|
||||
}
|
||||
_transferOwnership(initialOwner);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the address of the current owner.
|
||||
*/
|
||||
function owner() external view virtual returns (address) {
|
||||
return _owner;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Leaves the contract without owner. It will not be possible to call
|
||||
* `onlyOwner` functions. Can only be called by the current owner.
|
||||
*
|
||||
* NOTE: Renouncing ownership will leave the contract without an owner,
|
||||
* thereby disabling any functionality that is only available to the owner.
|
||||
*/
|
||||
function renounceOwnership() external virtual onlyOwner {
|
||||
_transferOwnership(address(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Transfers ownership of the contract to a new account (`newOwner`).
|
||||
* Can only be called by the current owner.
|
||||
*/
|
||||
function transferOwnership(address newOwner) external virtual onlyOwner {
|
||||
if (newOwner == address(0)) {
|
||||
revert OwnableInvalidOwner(address(0));
|
||||
}
|
||||
_transferOwnership(newOwner);
|
||||
}
|
||||
|
||||
}
|
||||
41
src/OwnableInternal.sol
Normal file
41
src/OwnableInternal.sol
Normal file
@@ -0,0 +1,41 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
|
||||
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import {Context} from "../lib/openzeppelin-contracts/contracts/utils/Context.sol";
|
||||
import {IOwnable} from "./IOwnable.sol";
|
||||
|
||||
/**
|
||||
* @dev OpenZeppelin's Ownable contract, split into internal and external parts.
|
||||
*/
|
||||
abstract contract OwnableInternal is Context {
|
||||
address internal _owner;
|
||||
|
||||
/**
|
||||
* @dev Throws if called by any account other than the owner.
|
||||
*/
|
||||
modifier onlyOwner() {
|
||||
_checkOwner();
|
||||
_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Throws if the sender is not the owner.
|
||||
*/
|
||||
function _checkOwner() internal view virtual {
|
||||
if (_owner != _msgSender()) {
|
||||
revert IOwnable.OwnableUnauthorizedAccount(_msgSender());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Transfers ownership of the contract to a new account (`newOwner`).
|
||||
* Internal function without access restriction.
|
||||
*/
|
||||
function _transferOwnership(address newOwner) internal virtual {
|
||||
address oldOwner = _owner;
|
||||
_owner = newOwner;
|
||||
emit IOwnable.OwnershipTransferred(oldOwner, newOwner);
|
||||
}
|
||||
}
|
||||
@@ -5,15 +5,17 @@ import {IERC20} from "../lib/openzeppelin-contracts/contracts/token/ERC20/IERC20
|
||||
import {SafeERC20} from "../lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
|
||||
import {IPartyPlanner} from "./IPartyPlanner.sol";
|
||||
import {IPartyPool} from "./IPartyPool.sol";
|
||||
import {NativeWrapper} from "./NativeWrapper.sol";
|
||||
import {LMSRStabilized} from "./LMSRStabilized.sol";
|
||||
import {NativeWrapper} from "./NativeWrapper.sol";
|
||||
import {OwnableExternal} from "./OwnableExternal.sol";
|
||||
import {OwnableInternal} from "./OwnableInternal.sol";
|
||||
import {IPartyPoolDeployer} from "./PartyPoolDeployer.sol";
|
||||
import {PartyPoolMintImpl} from "./PartyPoolMintImpl.sol";
|
||||
import {PartyPoolSwapImpl} from "./PartyPoolSwapImpl.sol";
|
||||
|
||||
/// @title PartyPlanner
|
||||
/// @notice Factory contract for creating and tracking PartyPool instances
|
||||
contract PartyPlanner is IPartyPlanner {
|
||||
contract PartyPlanner is OwnableExternal, IPartyPlanner {
|
||||
using SafeERC20 for IERC20;
|
||||
int128 private constant ONE = int128(1) << 64;
|
||||
|
||||
@@ -46,32 +48,37 @@ contract PartyPlanner is IPartyPlanner {
|
||||
mapping(IERC20 => bool) private _tokenSupported;
|
||||
mapping(IERC20 => IPartyPool[]) private _poolsByToken;
|
||||
|
||||
/// @param _swapImpl address of the Swap implementation contract to be used by all pools
|
||||
/// @param _mintImpl address of the Mint implementation contract to be used by all pools
|
||||
/// @param _protocolFeePpm protocol fee share (ppm) to be used for pools created by this planner
|
||||
/// @param _protocolFeeAddress recipient address for protocol fees for pools created by this planner (may be address(0))
|
||||
/// @param owner_ Initial administrator who is allowed to create new pools and kill() old ones
|
||||
/// @param wrapper_ The WETH9 implementation address used for this chain
|
||||
/// @param swapImpl_ address of the Swap implementation contract to be used by all pools
|
||||
/// @param mintImpl_ address of the Mint implementation contract to be used by all pools
|
||||
/// @param protocolFeePpm_ protocol fee share (ppm) to be used for pools created by this planner
|
||||
/// @param protocolFeeAddress_ recipient address for protocol fees for pools created by this planner (may be address(0))
|
||||
constructor(
|
||||
NativeWrapper _wrapper,
|
||||
PartyPoolSwapImpl _swapImpl,
|
||||
PartyPoolMintImpl _mintImpl,
|
||||
IPartyPoolDeployer _deployer,
|
||||
IPartyPoolDeployer _balancedPairDeployer,
|
||||
uint256 _protocolFeePpm,
|
||||
address _protocolFeeAddress
|
||||
) {
|
||||
WRAPPER = _wrapper;
|
||||
require(address(_swapImpl) != address(0), "Planner: swapImpl address cannot be zero");
|
||||
SWAP_IMPL = _swapImpl;
|
||||
require(address(_mintImpl) != address(0), "Planner: mintImpl address cannot be zero");
|
||||
MINT_IMPL = _mintImpl;
|
||||
require(address(_deployer) != address(0), "Planner: deployer address cannot be zero");
|
||||
NORMAL_POOL_DEPLOYER = _deployer;
|
||||
require(address(_balancedPairDeployer) != address(0), "Planner: balanced pair deployer address cannot be zero");
|
||||
BALANCED_PAIR_DEPLOYER = _balancedPairDeployer;
|
||||
address owner_,
|
||||
NativeWrapper wrapper_,
|
||||
PartyPoolSwapImpl swapImpl_,
|
||||
PartyPoolMintImpl mintImpl_,
|
||||
IPartyPoolDeployer deployer_,
|
||||
IPartyPoolDeployer balancedPairDeployer_,
|
||||
uint256 protocolFeePpm_,
|
||||
address protocolFeeAddress_
|
||||
)
|
||||
OwnableExternal(owner_)
|
||||
{
|
||||
WRAPPER = wrapper_;
|
||||
require(address(swapImpl_) != address(0), "Planner: swapImpl address cannot be zero");
|
||||
SWAP_IMPL = swapImpl_;
|
||||
require(address(mintImpl_) != address(0), "Planner: mintImpl address cannot be zero");
|
||||
MINT_IMPL = mintImpl_;
|
||||
require(address(deployer_) != address(0), "Planner: deployer address cannot be zero");
|
||||
NORMAL_POOL_DEPLOYER = deployer_;
|
||||
require(address(balancedPairDeployer_) != address(0), "Planner: balanced pair deployer address cannot be zero");
|
||||
BALANCED_PAIR_DEPLOYER = balancedPairDeployer_;
|
||||
|
||||
require(_protocolFeePpm < 1_000_000, "Planner: protocol fee >= ppm");
|
||||
PROTOCOL_FEE_PPM = _protocolFeePpm;
|
||||
PROTOCOL_FEE_ADDRESS = _protocolFeeAddress;
|
||||
require(protocolFeePpm_ < 1_000_000, "Planner: protocol fee >= ppm");
|
||||
PROTOCOL_FEE_PPM = protocolFeePpm_;
|
||||
PROTOCOL_FEE_ADDRESS = protocolFeeAddress_;
|
||||
}
|
||||
|
||||
/// Main newPool variant: accepts kappa directly (preferred).
|
||||
@@ -79,38 +86,39 @@ contract PartyPlanner is IPartyPlanner {
|
||||
// Pool constructor args
|
||||
string memory name_,
|
||||
string memory symbol_,
|
||||
IERC20[] memory _tokens,
|
||||
uint256[] memory _bases,
|
||||
int128 _kappa,
|
||||
uint256 _swapFeePpm,
|
||||
uint256 _flashFeePpm,
|
||||
bool _stable,
|
||||
IERC20[] memory tokens_,
|
||||
uint256[] memory bases_,
|
||||
int128 kappa_,
|
||||
uint256 swapFeePpm_,
|
||||
uint256 flashFeePpm_,
|
||||
bool stable_,
|
||||
// Initial deposit information
|
||||
address payer,
|
||||
address receiver,
|
||||
uint256[] memory initialDeposits,
|
||||
uint256 initialLpAmount,
|
||||
uint256 deadline
|
||||
) public returns (IPartyPool pool, uint256 lpAmount) {
|
||||
) public onlyOwner returns (IPartyPool pool, uint256 lpAmount) {
|
||||
// Validate inputs
|
||||
require(deadline == 0 || block.timestamp <= deadline, "Planner: deadline exceeded");
|
||||
require(_tokens.length == initialDeposits.length, "Planner: tokens and deposits length mismatch");
|
||||
require(tokens_.length == initialDeposits.length, "Planner: tokens and deposits length mismatch");
|
||||
require(payer != address(0), "Planner: payer cannot be zero address");
|
||||
require(receiver != address(0), "Planner: receiver cannot be zero address");
|
||||
|
||||
// Validate kappa > 0 (Q64.64)
|
||||
require(_kappa > int128(0), "Planner: kappa must be > 0");
|
||||
require(kappa_ > int128(0), "Planner: kappa must be > 0");
|
||||
|
||||
// Create a new PartyPool instance (kappa-based constructor)
|
||||
IPartyPoolDeployer deployer = _stable && _tokens.length == 2 ? BALANCED_PAIR_DEPLOYER : NORMAL_POOL_DEPLOYER;
|
||||
IPartyPoolDeployer deployer = stable_ && tokens_.length == 2 ? BALANCED_PAIR_DEPLOYER : NORMAL_POOL_DEPLOYER;
|
||||
pool = deployer.deploy(
|
||||
_owner, // Same owner as this PartyPlanner
|
||||
name_,
|
||||
symbol_,
|
||||
_tokens,
|
||||
_bases,
|
||||
_kappa,
|
||||
_swapFeePpm,
|
||||
_flashFeePpm,
|
||||
tokens_,
|
||||
bases_,
|
||||
kappa_,
|
||||
swapFeePpm_,
|
||||
flashFeePpm_,
|
||||
PROTOCOL_FEE_PPM,
|
||||
PROTOCOL_FEE_ADDRESS,
|
||||
WRAPPER,
|
||||
@@ -122,8 +130,8 @@ contract PartyPlanner is IPartyPlanner {
|
||||
_poolSupported[pool] = true;
|
||||
|
||||
// Track _tokens and populate mappings
|
||||
for (uint256 i = 0; i < _tokens.length; i++) {
|
||||
IERC20 token = _tokens[i];
|
||||
for (uint256 i = 0; i < tokens_.length; i++) {
|
||||
IERC20 token = tokens_[i];
|
||||
|
||||
// Add token to _allTokens if not already present
|
||||
if (!_tokenSupported[token]) {
|
||||
@@ -135,17 +143,17 @@ contract PartyPlanner is IPartyPlanner {
|
||||
_poolsByToken[token].push(pool);
|
||||
}
|
||||
|
||||
emit PartyStarted(pool, name_, symbol_, _tokens);
|
||||
emit PartyStarted(pool, name_, symbol_, tokens_);
|
||||
|
||||
// Transfer initial _tokens from payer to the pool
|
||||
for (uint256 i = 0; i < _tokens.length; i++) {
|
||||
for (uint256 i = 0; i < tokens_.length; i++) {
|
||||
if (initialDeposits[i] > 0) {
|
||||
IERC20(_tokens[i]).safeTransferFrom(payer, address(pool), initialDeposits[i]);
|
||||
require(IERC20(_tokens[i]).balanceOf(address(pool)) == initialDeposits[i], 'fee-on-transfer tokens not supported');
|
||||
IERC20(tokens_[i]).safeTransferFrom(payer, address(pool), initialDeposits[i]);
|
||||
require(IERC20(tokens_[i]).balanceOf(address(pool)) == initialDeposits[i], 'fee-on-transfer tokens not supported');
|
||||
}
|
||||
}
|
||||
|
||||
// Call mint on the new pool to initialize it with the transferred _tokens
|
||||
// Call mint on the new pool to initialize it with the transferred tokens_
|
||||
lpAmount = pool.initialMint(receiver, initialLpAmount);
|
||||
}
|
||||
|
||||
@@ -156,37 +164,37 @@ contract PartyPlanner is IPartyPlanner {
|
||||
// Pool constructor args (old signature)
|
||||
string memory name_,
|
||||
string memory symbol_,
|
||||
IERC20[] memory _tokens,
|
||||
uint256[] memory _bases,
|
||||
int128 _tradeFrac,
|
||||
int128 _targetSlippage,
|
||||
uint256 _swapFeePpm,
|
||||
uint256 _flashFeePpm,
|
||||
bool _stable,
|
||||
IERC20[] memory tokens_,
|
||||
uint256[] memory bases_,
|
||||
int128 tradeFrac_,
|
||||
int128 targetSlippage_,
|
||||
uint256 swapFeePpm_,
|
||||
uint256 flashFeePpm_,
|
||||
bool stable_,
|
||||
// Initial deposit information
|
||||
address payer,
|
||||
address receiver,
|
||||
uint256[] memory initialDeposits,
|
||||
uint256 initialLpAmount,
|
||||
uint256 deadline
|
||||
) external returns (IPartyPool pool, uint256 lpAmount) {
|
||||
) external onlyOwner returns (IPartyPool pool, uint256 lpAmount) {
|
||||
// Validate fixed-point fractions: must be less than 1.0 in 64.64 fixed-point
|
||||
require(_tradeFrac < ONE, "Planner: tradeFrac must be < 1 (64.64)");
|
||||
require(_targetSlippage < ONE, "Planner: targetSlippage must be < 1 (64.64)");
|
||||
require(tradeFrac_ < ONE, "Planner: tradeFrac must be < 1 (64.64)");
|
||||
require(targetSlippage_ < ONE, "Planner: targetSlippage must be < 1 (64.64)");
|
||||
|
||||
// Compute kappa from slippage params using LMSR helper (kappa depends only on n, f and s)
|
||||
int128 computedKappa = LMSRStabilized.computeKappaFromSlippage(_tokens.length, _tradeFrac, _targetSlippage);
|
||||
int128 computedKappa = LMSRStabilized.computeKappaFromSlippage(tokens_.length, tradeFrac_, targetSlippage_);
|
||||
|
||||
// Delegate to the kappa-based newPool variant
|
||||
return newPool(
|
||||
name_,
|
||||
symbol_,
|
||||
_tokens,
|
||||
_bases,
|
||||
tokens_,
|
||||
bases_,
|
||||
computedKappa,
|
||||
_swapFeePpm,
|
||||
_flashFeePpm,
|
||||
_stable,
|
||||
swapFeePpm_,
|
||||
flashFeePpm_,
|
||||
stable_,
|
||||
payer,
|
||||
receiver,
|
||||
initialDeposits,
|
||||
|
||||
@@ -18,27 +18,33 @@ import {ReentrancyGuard} from "../lib/openzeppelin-contracts/contracts/utils/Ree
|
||||
import {SafeERC20} from "../lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
|
||||
import {IERC3156FlashLender} from "../lib/openzeppelin-contracts/contracts/interfaces/IERC3156FlashLender.sol";
|
||||
import {NativeWrapper} from "./NativeWrapper.sol";
|
||||
import {OwnableExternal} from "./OwnableExternal.sol";
|
||||
|
||||
/// @title PartyPool - LMSR-backed multi-asset pool with LP ERC20 token
|
||||
/// @notice A multi-asset liquidity pool backed by the LMSRStabilized pricing model.
|
||||
/// The pool issues an ERC20 LP token representing proportional ownership.
|
||||
/// It supports:
|
||||
/// - Proportional minting and burning of LP _tokens,
|
||||
/// - Single-token mint (swapMint) and single-asset withdrawal (burnSwap),
|
||||
/// - Proportional minting and burning of LP tokens,
|
||||
/// - Exact-input swaps and swaps-to-price-limits,
|
||||
/// - Flash loans via a callback interface.
|
||||
/// - Single-token mint (swapMint) and single-asset withdrawal (burnSwap),
|
||||
/// - ERC-3156 flash loans
|
||||
///
|
||||
/// @dev The contract stores per-token uint "_bases" used to scale token units into the internal Q64.64
|
||||
/// representation used by the LMSR library. Cached on-chain uint balances are kept to reduce balanceOf calls.
|
||||
/// @dev The contract stores per-token uint `_bases` used to scale token units into the internal Q64.64
|
||||
/// representation used by the LMSR library. Cached on-chain uint balances are kept to reduce balanceOf() calls.
|
||||
/// The contract uses ceiling/floor rules described in function comments to bias rounding in favor of the pool
|
||||
/// (i.e., floor outputs to users, ceil inputs/fees where appropriate).
|
||||
contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
|
||||
/// (i.e., floor outputs to users, ceil inputs/fees where appropriate). Mutating methods have re-entrancy locks.
|
||||
/// The contract may be "killed" by the admin in case any security issue is discovered, in which case all swaps and
|
||||
/// mints are disabled, and only the burn() method remains functional to allow LP's to withdraw their assets.
|
||||
contract PartyPool is PartyPoolBase, OwnableExternal, ERC20External, IPartyPool {
|
||||
using ABDKMath64x64 for int128;
|
||||
using LMSRStabilized for LMSRStabilized.State;
|
||||
using SafeERC20 for IERC20;
|
||||
|
||||
receive() external payable {}
|
||||
|
||||
/// @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; }
|
||||
|
||||
/// @notice Liquidity parameter κ (Q64.64) used by the LMSR kernel: b = κ * S(q)
|
||||
@@ -90,6 +96,7 @@ contract PartyPool is PartyPoolBase, ERC20External, 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)
|
||||
@@ -100,6 +107,7 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
|
||||
/// @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_,
|
||||
@@ -114,8 +122,10 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
|
||||
PartyPoolMintImpl mintImpl_
|
||||
)
|
||||
PartyPoolBase(wrapperToken_)
|
||||
OwnableExternal(owner_)
|
||||
ERC20External(name_, symbol_)
|
||||
{
|
||||
require(owner_ != address(0));
|
||||
require(tokens_.length > 1, "Pool: need >1 asset");
|
||||
require(tokens_.length == bases_.length, "Pool: lengths mismatch");
|
||||
_tokens = tokens_;
|
||||
@@ -149,6 +159,11 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
|
||||
_protocolFeesOwed = new uint256[](n);
|
||||
}
|
||||
|
||||
/// @notice If a security problem is found, the vault owner may call this function to permanently disable swap and
|
||||
/// mint functionality, leaving only burns (withdrawals) working.
|
||||
function kill() external onlyOwner {
|
||||
_killed = true;
|
||||
}
|
||||
|
||||
/* ----------------------
|
||||
Initialization / Mint / Burn (LP token managed)
|
||||
@@ -225,7 +240,7 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
|
||||
int128 limitPrice,
|
||||
uint256 deadline,
|
||||
bool unwrap
|
||||
) external payable native nonReentrant returns (uint256 amountIn, uint256 amountOut, uint256 fee) {
|
||||
) external payable native nonReentrant killable returns (uint256 amountIn, uint256 amountOut, uint256 fee) {
|
||||
require(deadline == 0 || block.timestamp <= deadline, "swap: deadline exceeded");
|
||||
|
||||
// Compute amounts using the same path as views
|
||||
@@ -425,7 +440,7 @@ contract PartyPool is PartyPoolBase, ERC20External, IPartyPool {
|
||||
address tokenAddr,
|
||||
uint256 amount,
|
||||
bytes calldata data
|
||||
) external nonReentrant returns (bool)
|
||||
) external nonReentrant killable returns (bool)
|
||||
{
|
||||
IERC20 token = IERC20(tokenAddr);
|
||||
require(amount <= token.balanceOf(address(this)));
|
||||
|
||||
@@ -11,6 +11,7 @@ import {PartyPoolSwapImpl} from "./PartyPoolSwapImpl.sol";
|
||||
|
||||
contract PartyPoolBalancedPair is PartyPool {
|
||||
constructor(
|
||||
address owner_,
|
||||
string memory name_,
|
||||
string memory symbol_,
|
||||
IERC20[] memory tokens_,
|
||||
@@ -23,7 +24,8 @@ contract PartyPoolBalancedPair is PartyPool {
|
||||
NativeWrapper wrapperToken_,
|
||||
PartyPoolSwapImpl swapMintImpl_,
|
||||
PartyPoolMintImpl mintImpl_
|
||||
) PartyPool(name_, symbol_, tokens_, bases_, kappa_, swapFeePpm_, flashFeePpm_, protocolFeePpm_, protocolFeeAddress_, wrapperToken_, swapMintImpl_, mintImpl_)
|
||||
)
|
||||
PartyPool(owner_, name_, symbol_, tokens_, bases_, kappa_, swapFeePpm_, flashFeePpm_, protocolFeePpm_, protocolFeeAddress_, wrapperToken_, swapMintImpl_, mintImpl_)
|
||||
{}
|
||||
|
||||
function _swapAmountsForExactInput(uint256 i, uint256 j, int128 a, int128 limitPrice) internal virtual override view
|
||||
|
||||
@@ -9,10 +9,11 @@ import {LMSRStabilized} from "./LMSRStabilized.sol";
|
||||
import {PartyPoolHelpers} from "./PartyPoolHelpers.sol";
|
||||
import {ReentrancyGuard} from "../lib/openzeppelin-contracts/contracts/utils/ReentrancyGuard.sol";
|
||||
import {SafeERC20} from "../lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
|
||||
import {OwnableInternal} from "./OwnableInternal.sol";
|
||||
|
||||
/// @notice Abstract base contract that contains storage and internal helpers only.
|
||||
/// No external/public functions or constructor here — derived implementations own immutables and constructors.
|
||||
abstract contract PartyPoolBase is ERC20Internal, ReentrancyGuard, PartyPoolHelpers {
|
||||
/// No external/public functions here.
|
||||
abstract contract PartyPoolBase is OwnableInternal, ERC20Internal, ReentrancyGuard, PartyPoolHelpers {
|
||||
using ABDKMath64x64 for int128;
|
||||
using LMSRStabilized for LMSRStabilized.State;
|
||||
using SafeERC20 for IERC20;
|
||||
@@ -23,18 +24,13 @@ abstract contract PartyPoolBase is ERC20Internal, ReentrancyGuard, PartyPoolHelp
|
||||
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
|
||||
//
|
||||
|
||||
//
|
||||
// Internal state (no immutables here; immutables belong to derived contracts)
|
||||
//
|
||||
/// @notice If _killed is set, then all `killable` methods are permanently disabled, leaving only burns
|
||||
/// (withdrawals) working
|
||||
bool internal _killed;
|
||||
|
||||
// LMSR internal state
|
||||
LMSRStabilized.State internal _lmsr;
|
||||
@@ -64,6 +60,20 @@ abstract contract PartyPoolBase is ERC20Internal, ReentrancyGuard, PartyPoolHelp
|
||||
uint256[] internal _cachedUintBalances;
|
||||
|
||||
|
||||
/// @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);
|
||||
}
|
||||
|
||||
modifier killable() {
|
||||
require(!_killed, 'killed');
|
||||
_;
|
||||
}
|
||||
|
||||
/* ----------------------
|
||||
Conversion & fee helpers (internal)
|
||||
---------------------- */
|
||||
|
||||
@@ -11,6 +11,7 @@ import {PartyPoolBalancedPair} from "./PartyPoolBalancedPair.sol";
|
||||
|
||||
interface IPartyPoolDeployer {
|
||||
function deploy(
|
||||
address owner_,
|
||||
string memory name_,
|
||||
string memory symbol_,
|
||||
IERC20[] memory tokens_,
|
||||
@@ -28,6 +29,7 @@ interface IPartyPoolDeployer {
|
||||
|
||||
contract PartyPoolDeployer is IPartyPoolDeployer {
|
||||
function deploy(
|
||||
address owner_,
|
||||
string memory name_,
|
||||
string memory symbol_,
|
||||
IERC20[] memory tokens_,
|
||||
@@ -42,6 +44,7 @@ contract PartyPoolDeployer is IPartyPoolDeployer {
|
||||
PartyPoolMintImpl mintImpl_
|
||||
) external returns (IPartyPool) {
|
||||
return new PartyPool(
|
||||
owner_,
|
||||
name_,
|
||||
symbol_,
|
||||
tokens_,
|
||||
@@ -60,6 +63,7 @@ contract PartyPoolDeployer is IPartyPoolDeployer {
|
||||
|
||||
contract PartyPoolBalancedPairDeployer is IPartyPoolDeployer {
|
||||
function deploy(
|
||||
address owner_,
|
||||
string memory name_,
|
||||
string memory symbol_,
|
||||
IERC20[] memory tokens_,
|
||||
@@ -74,6 +78,7 @@ contract PartyPoolBalancedPairDeployer is IPartyPoolDeployer {
|
||||
PartyPoolMintImpl mintImpl_
|
||||
) external returns (IPartyPool) {
|
||||
return new PartyPoolBalancedPair(
|
||||
owner_,
|
||||
name_,
|
||||
symbol_,
|
||||
tokens_,
|
||||
|
||||
@@ -25,7 +25,7 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
||||
// Initialization Mint
|
||||
//
|
||||
|
||||
function initialMint(address receiver, uint256 lpTokens, int128 KAPPA) external payable native nonReentrant
|
||||
function initialMint(address receiver, uint256 lpTokens, int128 KAPPA) external payable native killable nonReentrant
|
||||
returns (uint256 lpMinted) {
|
||||
uint256 n = _tokens.length;
|
||||
|
||||
@@ -65,7 +65,7 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
||||
// Regular Mint and Burn
|
||||
//
|
||||
|
||||
function mint(address payer, address receiver, uint256 lpTokenAmount, uint256 deadline) external payable native nonReentrant
|
||||
function mint(address payer, address receiver, uint256 lpTokenAmount, uint256 deadline) external payable native killable nonReentrant
|
||||
returns (uint256 lpMinted) {
|
||||
require(deadline == 0 || block.timestamp <= deadline, "mint: deadline exceeded");
|
||||
uint256 n = _tokens.length;
|
||||
@@ -131,7 +131,8 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
||||
return actualLpToMint;
|
||||
}
|
||||
|
||||
/// @notice Burn LP _tokens and withdraw the proportional basket to receiver.
|
||||
/// @notice Burn LP _tokens and withdraw the proportional basket to receiver. Functional even if the pool has been
|
||||
/// killed.
|
||||
/// @dev Payer must own or approve the LP _tokens being burned. The function updates LMSR state
|
||||
/// proportionally to reflect the reduced pool size after the withdrawal.
|
||||
/// @param payer address that provides the LP _tokens to burn
|
||||
@@ -345,7 +346,7 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
||||
uint256 deadline,
|
||||
uint256 swapFeePpm,
|
||||
uint256 protocolFeePpm
|
||||
) external payable native nonReentrant returns (uint256 lpMinted) {
|
||||
) external payable native killable nonReentrant returns (uint256 lpMinted) {
|
||||
uint256 n = _tokens.length;
|
||||
require(inputTokenIndex < n, "swapMint: idx");
|
||||
require(maxAmountIn > 0, "swapMint: input zero");
|
||||
@@ -468,6 +469,8 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
||||
}
|
||||
|
||||
/// @notice Burn LP _tokens then swap the redeemed proportional basket into a single asset `inputTokenIndex` and send to receiver.
|
||||
/// This version of burn does not work if the vault has been killed, because it involves a swap. Use regular burn()
|
||||
/// to recover funds if the pool has been killed.
|
||||
/// @dev The function burns LP _tokens (authorization via allowance if needed), sends the single-asset payout and updates LMSR state.
|
||||
/// @param payer who burns LP _tokens
|
||||
/// @param receiver who receives the single asset
|
||||
@@ -485,7 +488,7 @@ contract PartyPoolMintImpl is PartyPoolBase {
|
||||
bool unwrap,
|
||||
uint256 swapFeePpm,
|
||||
uint256 protocolFeePpm
|
||||
) external nonReentrant returns (uint256 amountOutUint) {
|
||||
) external nonReentrant killable returns (uint256 amountOutUint) {
|
||||
uint256 n = _tokens.length;
|
||||
require(inputTokenIndex < n, "burnSwap: idx");
|
||||
require(lpAmount > 0, "burnSwap: zero lp");
|
||||
|
||||
@@ -59,7 +59,7 @@ contract PartyPoolSwapImpl is PartyPoolBase {
|
||||
bool unwrap,
|
||||
uint256 swapFeePpm,
|
||||
uint256 protocolFeePpm
|
||||
) external payable native returns (uint256 amountInUsed, uint256 amountOut, uint256 fee) {
|
||||
) external payable native killable nonReentrant returns (uint256 amountInUsed, uint256 amountOut, uint256 fee) {
|
||||
uint256 n = _tokens.length;
|
||||
require(inputTokenIndex < n && outputTokenIndex < n, "swapToLimit: idx");
|
||||
require(limitPrice > int128(0), "swapToLimit: limit <= 0");
|
||||
|
||||
@@ -18,11 +18,17 @@ library Deploy {
|
||||
|
||||
function newPartyPlanner() internal returns (PartyPlanner) {
|
||||
NativeWrapper wrapper = new WETH9();
|
||||
return newPartyPlanner(wrapper);
|
||||
return newPartyPlanner(msg.sender, wrapper);
|
||||
}
|
||||
|
||||
function newPartyPlanner(NativeWrapper wrapper) internal returns (PartyPlanner) {
|
||||
function newPartyPlanner(address owner) internal returns (PartyPlanner) {
|
||||
NativeWrapper wrapper = new WETH9();
|
||||
return newPartyPlanner(owner, wrapper);
|
||||
}
|
||||
|
||||
function newPartyPlanner(address owner, NativeWrapper wrapper) internal returns (PartyPlanner) {
|
||||
return new PartyPlanner(
|
||||
owner,
|
||||
wrapper,
|
||||
new PartyPoolSwapImpl(wrapper),
|
||||
new PartyPoolMintImpl(wrapper),
|
||||
@@ -34,6 +40,7 @@ library Deploy {
|
||||
}
|
||||
|
||||
function newPartyPool(
|
||||
address owner_,
|
||||
string memory name_,
|
||||
string memory symbol_,
|
||||
IERC20[] memory tokens_,
|
||||
@@ -44,10 +51,11 @@ library Deploy {
|
||||
bool _stable
|
||||
) internal returns (PartyPool) {
|
||||
NativeWrapper wrapper = new WETH9();
|
||||
return newPartyPool(name_, symbol_, tokens_, bases_, _kappa, _swapFeePpm, _flashFeePpm, wrapper, _stable);
|
||||
return newPartyPool(owner_, name_, symbol_, tokens_, bases_, _kappa, _swapFeePpm, _flashFeePpm, wrapper, _stable);
|
||||
}
|
||||
|
||||
function newPartyPool(
|
||||
address owner_,
|
||||
string memory name_,
|
||||
string memory symbol_,
|
||||
IERC20[] memory tokens_,
|
||||
@@ -60,6 +68,7 @@ library Deploy {
|
||||
) internal returns (PartyPool) {
|
||||
return _stable && tokens_.length == 2 ?
|
||||
new PartyPoolBalancedPair(
|
||||
owner_,
|
||||
name_,
|
||||
symbol_,
|
||||
tokens_,
|
||||
@@ -74,6 +83,7 @@ library Deploy {
|
||||
new PartyPoolMintImpl(wrapper)
|
||||
) :
|
||||
new PartyPool(
|
||||
owner_,
|
||||
name_,
|
||||
symbol_,
|
||||
tokens_,
|
||||
|
||||
@@ -141,7 +141,7 @@ contract GasTest is Test {
|
||||
}
|
||||
// Compute kappa from slippage params and number of _tokens, then construct pool with kappa
|
||||
int128 computedKappa = LMSRStabilized.computeKappaFromSlippage(ierc20Tokens.length, tradeFrac, targetSlippage);
|
||||
PartyPool newPool = Deploy.newPartyPool(poolName, poolName, ierc20Tokens, bases, computedKappa, feePpm, feePpm, false);
|
||||
PartyPool newPool = Deploy.newPartyPool(address(this), poolName, poolName, ierc20Tokens, bases, computedKappa, feePpm, feePpm, false);
|
||||
|
||||
// Transfer initial deposit amounts into pool before initial mint
|
||||
for (uint256 i = 0; i < numTokens; i++) {
|
||||
@@ -181,7 +181,7 @@ contract GasTest is Test {
|
||||
ierc20Tokens[i] = IERC20(tokens[i]);
|
||||
}
|
||||
int128 computedKappa = LMSRStabilized.computeKappaFromSlippage(ierc20Tokens.length, tradeFrac, targetSlippage);
|
||||
PartyPool newPool = Deploy.newPartyPool(poolName, poolName, ierc20Tokens, bases, computedKappa, feePpm, feePpm, true);
|
||||
PartyPool newPool = Deploy.newPartyPool(address(this), poolName, poolName, ierc20Tokens, bases, computedKappa, feePpm, feePpm, true);
|
||||
|
||||
// Transfer initial deposit amounts into pool before initial mint
|
||||
for (uint256 i = 0; i < numTokens; i++) {
|
||||
|
||||
@@ -94,7 +94,7 @@ contract NativeTest is Test {
|
||||
uint256 feePpm = 1000;
|
||||
|
||||
int128 kappa = LMSRStabilized.computeKappaFromSlippage(tokens.length, tradeFrac, targetSlippage);
|
||||
pool = Deploy.newPartyPool("LP", "LP", tokens, bases, kappa, feePpm, feePpm, weth, false);
|
||||
pool = Deploy.newPartyPool(address(this), "LP", "LP", tokens, bases, kappa, feePpm, feePpm, weth, false);
|
||||
|
||||
// Transfer initial deposit amounts into pool
|
||||
token0.transfer(address(pool), INIT_BAL);
|
||||
|
||||
@@ -46,8 +46,8 @@ contract PartyPlannerTest is Test {
|
||||
uint256 constant INITIAL_DEPOSIT_AMOUNT = 1000e18;
|
||||
|
||||
function setUp() public {
|
||||
// Deploy PartyPlanner
|
||||
planner = Deploy.newPartyPlanner();
|
||||
// Deploy PartyPlanner owned by this test contract
|
||||
planner = Deploy.newPartyPlanner(address(this));
|
||||
|
||||
// Deploy mock _tokens
|
||||
tokenA = new MockERC20("Token A", "TKNA", 18);
|
||||
|
||||
@@ -173,7 +173,7 @@ contract PartyPoolTest is Test {
|
||||
uint256 feePpm = 1000;
|
||||
|
||||
int128 kappa3 = LMSRStabilized.computeKappaFromSlippage(tokens.length, tradeFrac, targetSlippage);
|
||||
pool = Deploy.newPartyPool("LP", "LP", tokens, bases, kappa3, feePpm, feePpm, false);
|
||||
pool = Deploy.newPartyPool(address(this), "LP", "LP", tokens, bases, kappa3, feePpm, feePpm, false);
|
||||
|
||||
// Transfer initial deposit amounts into pool before initial mint (pool expects _tokens already in contract)
|
||||
// We deposit equal amounts INIT_BAL for each token
|
||||
@@ -203,7 +203,7 @@ contract PartyPoolTest is Test {
|
||||
}
|
||||
|
||||
int128 kappa10 = LMSRStabilized.computeKappaFromSlippage(tokens10.length, tradeFrac, targetSlippage);
|
||||
pool10 = Deploy.newPartyPool("LP10", "LP10", tokens10, bases10, kappa10, feePpm, feePpm, false);
|
||||
pool10 = Deploy.newPartyPool(address(this), "LP10", "LP10", tokens10, bases10, kappa10, feePpm, feePpm, false);
|
||||
|
||||
// Mint additional _tokens for pool10 initial deposit
|
||||
token0.mint(address(this), INIT_BAL);
|
||||
@@ -992,11 +992,11 @@ contract PartyPoolTest is Test {
|
||||
|
||||
// Pool with default initialization (lpTokens = 0)
|
||||
int128 kappaDefault = LMSRStabilized.computeKappaFromSlippage(tokens.length, tradeFrac, targetSlippage);
|
||||
PartyPool poolDefault = Deploy.newPartyPool("LP_DEFAULT", "LP_DEFAULT", tokens, bases, kappaDefault, feePpm, feePpm, false);
|
||||
PartyPool poolDefault = Deploy.newPartyPool(address(this), "LP_DEFAULT", "LP_DEFAULT", tokens, bases, kappaDefault, feePpm, feePpm, false);
|
||||
|
||||
// Pool with custom initialization (lpTokens = custom amount)
|
||||
int128 kappaCustom = LMSRStabilized.computeKappaFromSlippage(tokens.length, tradeFrac, targetSlippage);
|
||||
PartyPool poolCustom = Deploy.newPartyPool("LP_CUSTOM", "LP_CUSTOM", tokens, bases, kappaCustom, feePpm, feePpm, false);
|
||||
PartyPool poolCustom = Deploy.newPartyPool(address(this), "LP_CUSTOM", "LP_CUSTOM", tokens, bases, kappaCustom, feePpm, feePpm, false);
|
||||
|
||||
// Mint additional _tokens for both pools
|
||||
token0.mint(address(this), INIT_BAL * 2);
|
||||
@@ -1068,9 +1068,9 @@ contract PartyPoolTest is Test {
|
||||
uint256 feePpm = 1000;
|
||||
|
||||
int128 kappaDefault2 = LMSRStabilized.computeKappaFromSlippage(tokens.length, tradeFrac, targetSlippage);
|
||||
PartyPool poolDefault = Deploy.newPartyPool("LP_DEFAULT", "LP_DEFAULT", tokens, bases, kappaDefault2, feePpm, feePpm, false);
|
||||
PartyPool poolDefault = Deploy.newPartyPool(address(this), "LP_DEFAULT", "LP_DEFAULT", tokens, bases, kappaDefault2, feePpm, feePpm, false);
|
||||
int128 kappaCustom2 = LMSRStabilized.computeKappaFromSlippage(tokens.length, tradeFrac, targetSlippage);
|
||||
PartyPool poolCustom = Deploy.newPartyPool("LP_CUSTOM", "LP_CUSTOM", tokens, bases, kappaCustom2, feePpm, feePpm, false);
|
||||
PartyPool poolCustom = Deploy.newPartyPool(address(this), "LP_CUSTOM", "LP_CUSTOM", tokens, bases, kappaCustom2, feePpm, feePpm, false);
|
||||
|
||||
// Mint additional _tokens
|
||||
token0.mint(address(this), INIT_BAL * 4);
|
||||
|
||||
Reference in New Issue
Block a user