linted
This commit is contained in:
@@ -13,6 +13,6 @@ gas_reports = ['PartyPool', 'PartyPlanner', 'PartyPoolSwapMintImpl', 'PartyPoolM
|
|||||||
fs_permissions = [{ access = "write", path = "chain.json"}]
|
fs_permissions = [{ access = "write", path = "chain.json"}]
|
||||||
|
|
||||||
[lint]
|
[lint]
|
||||||
exclude_lints=['mixed-case-variable', 'unaliased-plain-import', ]
|
lint_on_build=false # more annoying than helpful
|
||||||
|
|
||||||
# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options
|
# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ import "forge-std/console2.sol";
|
|||||||
|
|
||||||
contract DeployMock is Script {
|
contract DeployMock is Script {
|
||||||
|
|
||||||
address constant devAccount0 = 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266;
|
address constant public DEV_ACCOUNT_0 = 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266;
|
||||||
// private key 0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356
|
// private key 0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356
|
||||||
address constant devAccount7 = 0x14dC79964da2C08b23698B3D3cc7Ca32193d9955;
|
address constant public DEV_ACCOUNT_7 = 0x14dC79964da2C08b23698B3D3cc7Ca32193d9955;
|
||||||
|
|
||||||
function run() public {
|
function run() public {
|
||||||
vm.startBroadcast();
|
vm.startBroadcast();
|
||||||
@@ -69,14 +69,14 @@ contract DeployMock is Script {
|
|||||||
_feePpm,
|
_feePpm,
|
||||||
false,
|
false,
|
||||||
msg.sender, // payer: this script
|
msg.sender, // payer: this script
|
||||||
devAccount7, // receiver of initial LP
|
DEV_ACCOUNT_7, // receiver of initial LP
|
||||||
initialDeposits,
|
initialDeposits,
|
||||||
initialLpAmount,
|
initialLpAmount,
|
||||||
deadline
|
deadline
|
||||||
);
|
);
|
||||||
|
|
||||||
// give tokens to dev7 for later use
|
// give tokens to dev7 for later use
|
||||||
mintAll(devAccount7, 1_000_000);
|
mintAll(DEV_ACCOUNT_7, 1_000_000);
|
||||||
|
|
||||||
vm.stopBroadcast();
|
vm.stopBroadcast();
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ library Deploy {
|
|||||||
) internal returns (PartyPool) {
|
) internal returns (PartyPool) {
|
||||||
return new PartyPool(name_, symbol_, tokens_, bases_, _kappa, _swapFeePpm, _flashFeePpm, _stable,
|
return new PartyPool(name_, symbol_, tokens_, bases_, _kappa, _swapFeePpm, _flashFeePpm, _stable,
|
||||||
new PartyPoolSwapMintImpl(),
|
new PartyPoolSwapMintImpl(),
|
||||||
address(new PartyPoolMintImpl())
|
new PartyPoolMintImpl()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -117,6 +117,9 @@ interface IPartyPlanner {
|
|||||||
function getPoolsByToken(IERC20 token, uint256 offset, uint256 limit) external view returns (PartyPool[] memory pools);
|
function getPoolsByToken(IERC20 token, uint256 offset, uint256 limit) external view returns (PartyPool[] memory pools);
|
||||||
|
|
||||||
/// @notice Address of the SwapMint implementation contract used by all pools created by this factory
|
/// @notice Address of the SwapMint implementation contract used by all pools created by this factory
|
||||||
function swapMintImpl() external view returns (address);
|
function mintImpl() external view returns (PartyPoolMintImpl);
|
||||||
|
|
||||||
|
/// @notice Address of the SwapMint implementation contract used by all pools created by this factory
|
||||||
|
function swapMintImpl() external view returns (PartyPoolSwapMintImpl);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,13 +13,15 @@ import {PartyPoolMintImpl} from "./PartyPoolMintImpl.sol";
|
|||||||
/// @notice Factory contract for creating and tracking PartyPool instances
|
/// @notice Factory contract for creating and tracking PartyPool instances
|
||||||
contract PartyPlanner is IPartyPlanner {
|
contract PartyPlanner is IPartyPlanner {
|
||||||
using SafeERC20 for IERC20;
|
using SafeERC20 for IERC20;
|
||||||
int128 private constant FIXED_ONE_64x64 = int128(1) << 64;
|
int128 private constant ONE = int128(1) << 64;
|
||||||
|
|
||||||
/// @notice Address of the SwapMint implementation contract used by all pools created by this factory
|
|
||||||
address public immutable swapMintImpl;
|
|
||||||
|
|
||||||
/// @notice Address of the Mint implementation contract used by all pools created by this factory
|
/// @notice Address of the Mint implementation contract used by all pools created by this factory
|
||||||
address public immutable mintImpl;
|
PartyPoolMintImpl private immutable MINT_IMPL;
|
||||||
|
function mintImpl() external view returns (PartyPoolMintImpl) { return MINT_IMPL; }
|
||||||
|
|
||||||
|
/// @notice Address of the SwapMint implementation contract used by all pools created by this factory
|
||||||
|
PartyPoolSwapMintImpl private immutable SWAP_MINT_IMPL;
|
||||||
|
function swapMintImpl() external view returns (PartyPoolSwapMintImpl) { return SWAP_MINT_IMPL; }
|
||||||
|
|
||||||
// On-chain pool indexing
|
// On-chain pool indexing
|
||||||
PartyPool[] private _allPools;
|
PartyPool[] private _allPools;
|
||||||
@@ -32,9 +34,9 @@ contract PartyPlanner is IPartyPlanner {
|
|||||||
/// @param _mintImpl address of the Mint implementation contract to be used by all pools
|
/// @param _mintImpl address of the Mint implementation contract to be used by all pools
|
||||||
constructor(PartyPoolSwapMintImpl _swapMintImpl, PartyPoolMintImpl _mintImpl) {
|
constructor(PartyPoolSwapMintImpl _swapMintImpl, PartyPoolMintImpl _mintImpl) {
|
||||||
require(address(_swapMintImpl) != address(0), "Planner: swapMintImpl address cannot be zero");
|
require(address(_swapMintImpl) != address(0), "Planner: swapMintImpl address cannot be zero");
|
||||||
swapMintImpl = address(_swapMintImpl);
|
SWAP_MINT_IMPL = _swapMintImpl;
|
||||||
require(address(_mintImpl) != address(0), "Planner: mintImpl address cannot be zero");
|
require(address(_mintImpl) != address(0), "Planner: mintImpl address cannot be zero");
|
||||||
mintImpl = address(_mintImpl);
|
MINT_IMPL = _mintImpl;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Main newPool variant: accepts kappa directly (preferred).
|
/// Main newPool variant: accepts kappa directly (preferred).
|
||||||
@@ -74,8 +76,8 @@ contract PartyPlanner is IPartyPlanner {
|
|||||||
_swapFeePpm,
|
_swapFeePpm,
|
||||||
_flashFeePpm,
|
_flashFeePpm,
|
||||||
_stable,
|
_stable,
|
||||||
PartyPoolSwapMintImpl(swapMintImpl),
|
PartyPoolSwapMintImpl(SWAP_MINT_IMPL),
|
||||||
mintImpl
|
MINT_IMPL
|
||||||
);
|
);
|
||||||
|
|
||||||
_allPools.push(pool);
|
_allPools.push(pool);
|
||||||
@@ -130,8 +132,8 @@ contract PartyPlanner is IPartyPlanner {
|
|||||||
uint256 deadline
|
uint256 deadline
|
||||||
) external returns (PartyPool pool, uint256 lpAmount) {
|
) external returns (PartyPool pool, uint256 lpAmount) {
|
||||||
// Validate fixed-point fractions: must be less than 1.0 in 64.64 fixed-point
|
// Validate fixed-point fractions: must be less than 1.0 in 64.64 fixed-point
|
||||||
require(_tradeFrac < FIXED_ONE_64x64, "Planner: tradeFrac must be < 1 (64.64)");
|
require(_tradeFrac < ONE, "Planner: tradeFrac must be < 1 (64.64)");
|
||||||
require(_targetSlippage < FIXED_ONE_64x64, "Planner: targetSlippage 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)
|
// 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);
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import "./IPartyPool.sol";
|
|||||||
import "./IPartyFlashCallback.sol";
|
import "./IPartyFlashCallback.sol";
|
||||||
import "./PartyPoolBase.sol";
|
import "./PartyPoolBase.sol";
|
||||||
import {PartyPoolSwapMintImpl} from "./PartyPoolSwapMintImpl.sol";
|
import {PartyPoolSwapMintImpl} from "./PartyPoolSwapMintImpl.sol";
|
||||||
|
import {PartyPoolMintImpl} from "./PartyPoolMintImpl.sol";
|
||||||
|
|
||||||
/// @title PartyPool - LMSR-backed multi-asset pool with LP ERC20 token
|
/// @title PartyPool - LMSR-backed multi-asset pool with LP ERC20 token
|
||||||
/// @notice A multi-asset liquidity pool backed by the LMSRStabilized pricing model.
|
/// @notice A multi-asset liquidity pool backed by the LMSRStabilized pricing model.
|
||||||
@@ -34,22 +35,28 @@ contract PartyPool is PartyPoolBase, IPartyPool {
|
|||||||
/// @notice Liquidity parameter κ (Q64.64) used by the LMSR kernel: b = κ * S(q)
|
/// @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
|
/// @dev Pool is constructed with a fixed κ. Clients that previously passed tradeFrac/targetSlippage
|
||||||
/// should use LMSRStabilized.computeKappaFromSlippage(...) to derive κ and pass it here.
|
/// should use LMSRStabilized.computeKappaFromSlippage(...) to derive κ and pass it here.
|
||||||
int128 public immutable kappa; // kappa in Q64.64
|
int128 private immutable KAPPA; // kappa in Q64.64
|
||||||
|
function kappa() external view returns (int128) { return KAPPA; }
|
||||||
|
|
||||||
/// @notice Per-swap fee in parts-per-million (ppm). Fee is taken from input amounts before LMSR computations.
|
/// @notice Per-swap fee in parts-per-million (ppm). Fee is taken from input amounts before LMSR computations.
|
||||||
uint256 public immutable swapFeePpm;
|
uint256 private immutable SWAP_FEE_PPM;
|
||||||
|
function swapFeePpm() external view returns (uint256) { return SWAP_FEE_PPM; }
|
||||||
|
|
||||||
/// @notice Flash-loan fee in parts-per-million (ppm) applied to flash borrow amounts.
|
/// @notice Flash-loan fee in parts-per-million (ppm) applied to flash borrow amounts.
|
||||||
uint256 public immutable flashFeePpm;
|
uint256 private immutable FLASH_FEE_PPM;
|
||||||
|
function flashFeePpm() external view returns (uint256) { return FLASH_FEE_PPM; }
|
||||||
|
|
||||||
/// @notice If true and there are exactly two assets, an optimized 2-asset stable-pair path is used for some computations.
|
/// @notice If true and there are exactly two assets, an optimized 2-asset stable-pair path is used for some computations.
|
||||||
bool immutable private _stablePair; // if true, the optimized LMSRStabilizedBalancedPair optimization path is enabled
|
bool immutable private IS_STABLE_PAIR; // if true, the optimized LMSRStabilizedBalancedPair optimization path is enabled
|
||||||
|
|
||||||
/// @notice Address of the SwapMint implementation contract for delegatecall
|
|
||||||
address public immutable swapMintImpl;
|
|
||||||
|
|
||||||
/// @notice Address of the Mint implementation contract for delegatecall
|
/// @notice Address of the Mint implementation contract for delegatecall
|
||||||
address public immutable mintImpl;
|
PartyPoolMintImpl private immutable MINT_IMPL;
|
||||||
|
function mintImpl() external view returns (PartyPoolMintImpl) { return MINT_IMPL; }
|
||||||
|
|
||||||
|
/// @notice Address of the SwapMint implementation contract for delegatecall
|
||||||
|
PartyPoolSwapMintImpl private immutable SWAP_MINT_IMPL;
|
||||||
|
function swapMintImpl() external view returns (PartyPoolSwapMintImpl) { return SWAP_MINT_IMPL; }
|
||||||
|
|
||||||
|
|
||||||
/// @inheritdoc IPartyPool
|
/// @inheritdoc IPartyPool
|
||||||
function getToken(uint256 i) external view returns (IERC20) { return tokens[i]; }
|
function getToken(uint256 i) external view returns (IERC20) { return tokens[i]; }
|
||||||
@@ -67,38 +74,36 @@ contract PartyPool is PartyPoolBase, IPartyPool {
|
|||||||
/// @param symbol_ LP token symbol
|
/// @param symbol_ LP token symbol
|
||||||
/// @param tokens_ token addresses (n)
|
/// @param tokens_ token addresses (n)
|
||||||
/// @param bases_ scaling bases for each token (n) - used when converting to/from internal 64.64 amounts
|
/// @param bases_ scaling bases for each token (n) - used when converting to/from internal 64.64 amounts
|
||||||
/// @param _kappa liquidity parameter κ (Q64.64) used to derive b = κ * S(q)
|
/// @param kappa_ liquidity parameter κ (Q64.64) used to derive b = κ * S(q)
|
||||||
/// @param _swapFeePpm fee in parts-per-million, taken from swap input amounts before LMSR calculations
|
/// @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 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 stable_ if true and assets.length==2, then the optimization for 2-asset stablecoin pools is activated.
|
||||||
/// @param _swapMintImpl address of the SwapMint implementation contract
|
/// @param swapMintImpl_ address of the SwapMint implementation contract
|
||||||
/// @param _mintImpl address of the Mint implementation contract
|
/// @param mintImpl_ address of the Mint implementation contract
|
||||||
constructor(
|
constructor(
|
||||||
string memory name_,
|
string memory name_,
|
||||||
string memory symbol_,
|
string memory symbol_,
|
||||||
IERC20[] memory tokens_,
|
IERC20[] memory tokens_,
|
||||||
uint256[] memory bases_,
|
uint256[] memory bases_,
|
||||||
int128 _kappa,
|
int128 kappa_,
|
||||||
uint256 _swapFeePpm,
|
uint256 swapFeePpm_,
|
||||||
uint256 _flashFeePpm,
|
uint256 flashFeePpm_,
|
||||||
bool _stable,
|
bool stable_,
|
||||||
PartyPoolSwapMintImpl _swapMintImpl,
|
PartyPoolSwapMintImpl swapMintImpl_,
|
||||||
address _mintImpl
|
PartyPoolMintImpl mintImpl_
|
||||||
) PartyPoolBase(name_, symbol_) {
|
) PartyPoolBase(name_, symbol_) {
|
||||||
require(tokens_.length > 1, "Pool: need >1 asset");
|
require(tokens_.length > 1, "Pool: need >1 asset");
|
||||||
require(tokens_.length == bases_.length, "Pool: lengths mismatch");
|
require(tokens_.length == bases_.length, "Pool: lengths mismatch");
|
||||||
tokens = tokens_;
|
tokens = tokens_;
|
||||||
bases = bases_;
|
bases = bases_;
|
||||||
kappa = _kappa;
|
KAPPA = kappa_;
|
||||||
require(_swapFeePpm < 1_000_000, "Pool: fee >= ppm");
|
require(swapFeePpm_ < 1_000_000, "Pool: fee >= ppm");
|
||||||
swapFeePpm = _swapFeePpm;
|
SWAP_FEE_PPM = swapFeePpm_;
|
||||||
require(_flashFeePpm < 1_000_000, "Pool: flash fee >= ppm");
|
require(flashFeePpm_ < 1_000_000, "Pool: flash fee >= ppm");
|
||||||
flashFeePpm = _flashFeePpm;
|
FLASH_FEE_PPM = flashFeePpm_;
|
||||||
_stablePair = _stable && tokens_.length == 2;
|
IS_STABLE_PAIR = stable_ && tokens_.length == 2;
|
||||||
require(address(_swapMintImpl) != address(0), "Pool: swapMintImpl address zero");
|
SWAP_MINT_IMPL = swapMintImpl_;
|
||||||
swapMintImpl = address(_swapMintImpl);
|
MINT_IMPL = mintImpl_;
|
||||||
require(_mintImpl != address(0), "Pool: mintImpl address zero");
|
|
||||||
mintImpl = _mintImpl;
|
|
||||||
|
|
||||||
uint256 n = tokens_.length;
|
uint256 n = tokens_.length;
|
||||||
|
|
||||||
@@ -175,7 +180,7 @@ contract PartyPool is PartyPoolBase, IPartyPool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the stabilized LMSR state with provided kappa
|
// Initialize the stabilized LMSR state with provided kappa
|
||||||
lmsr.init(newQInternal, kappa);
|
lmsr.init(newQInternal, KAPPA);
|
||||||
|
|
||||||
// Compute actual LP tokens to mint based on size metric (scaled)
|
// Compute actual LP tokens to mint based on size metric (scaled)
|
||||||
if( lpTokens != 0 )
|
if( lpTokens != 0 )
|
||||||
@@ -206,7 +211,7 @@ contract PartyPool is PartyPoolBase, IPartyPool {
|
|||||||
deadline
|
deadline
|
||||||
);
|
);
|
||||||
|
|
||||||
bytes memory result = Address.functionDelegateCall(mintImpl, data);
|
bytes memory result = Address.functionDelegateCall(address(MINT_IMPL), data);
|
||||||
return abi.decode(result, (uint256));
|
return abi.decode(result, (uint256));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,7 +256,7 @@ contract PartyPool is PartyPoolBase, IPartyPool {
|
|||||||
deadline
|
deadline
|
||||||
);
|
);
|
||||||
|
|
||||||
Address.functionDelegateCall(mintImpl, data);
|
Address.functionDelegateCall(address(MINT_IMPL), data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------------
|
/* ----------------------
|
||||||
@@ -411,7 +416,7 @@ contract PartyPool is PartyPoolBase, IPartyPool {
|
|||||||
require(lmsr.nAssets > 0, "swap: empty pool");
|
require(lmsr.nAssets > 0, "swap: empty pool");
|
||||||
|
|
||||||
// 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, swapFeePpm);
|
(, uint256 netUintForSwap) = _computeFee(maxAmountIn, SWAP_FEE_PPM);
|
||||||
|
|
||||||
// Convert to internal (floor)
|
// Convert to internal (floor)
|
||||||
int128 deltaInternalI = _uintToInternalFloor(netUintForSwap, bases[inputTokenIndex]);
|
int128 deltaInternalI = _uintToInternalFloor(netUintForSwap, bases[inputTokenIndex]);
|
||||||
@@ -419,9 +424,9 @@ contract PartyPool is PartyPoolBase, IPartyPool {
|
|||||||
|
|
||||||
// Compute internal amounts using LMSR (exact-input with price limit)
|
// Compute internal amounts using LMSR (exact-input with price limit)
|
||||||
// if _stablePair is true, use the optimized path
|
// if _stablePair is true, use the optimized path
|
||||||
console2.log('stablepair optimization?', _stablePair);
|
console2.log('stablepair optimization?', IS_STABLE_PAIR);
|
||||||
(amountInInternalUsed, amountOutInternal) =
|
(amountInInternalUsed, amountOutInternal) =
|
||||||
_stablePair ? LMSRStabilizedBalancedPair.swapAmountsForExactInput(lmsr, inputTokenIndex, outputTokenIndex, deltaInternalI, limitPrice)
|
IS_STABLE_PAIR ? LMSRStabilizedBalancedPair.swapAmountsForExactInput(lmsr, inputTokenIndex, outputTokenIndex, deltaInternalI, limitPrice)
|
||||||
: lmsr.swapAmountsForExactInput(inputTokenIndex, outputTokenIndex, deltaInternalI, limitPrice);
|
: lmsr.swapAmountsForExactInput(inputTokenIndex, outputTokenIndex, deltaInternalI, limitPrice);
|
||||||
|
|
||||||
// Convert actual used input internal -> uint (ceil)
|
// Convert actual used input internal -> uint (ceil)
|
||||||
@@ -431,8 +436,8 @@ contract PartyPool is PartyPoolBase, IPartyPool {
|
|||||||
// Compute gross transfer including fee on the used input (ceil)
|
// Compute gross transfer including fee on the used input (ceil)
|
||||||
feeUint = 0;
|
feeUint = 0;
|
||||||
grossIn = amountInUintNoFee;
|
grossIn = amountInUintNoFee;
|
||||||
if (swapFeePpm > 0) {
|
if (SWAP_FEE_PPM > 0) {
|
||||||
feeUint = _ceilFee(amountInUintNoFee, swapFeePpm);
|
feeUint = _ceilFee(amountInUintNoFee, SWAP_FEE_PPM);
|
||||||
grossIn += feeUint;
|
grossIn += feeUint;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -479,8 +484,8 @@ contract PartyPool is PartyPoolBase, IPartyPool {
|
|||||||
|
|
||||||
feeUint = 0;
|
feeUint = 0;
|
||||||
grossIn = amountInUintNoFee;
|
grossIn = amountInUintNoFee;
|
||||||
if (swapFeePpm > 0) {
|
if (SWAP_FEE_PPM > 0) {
|
||||||
feeUint = _ceilFee(amountInUintNoFee, swapFeePpm);
|
feeUint = _ceilFee(amountInUintNoFee, SWAP_FEE_PPM);
|
||||||
grossIn += feeUint;
|
grossIn += feeUint;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -491,17 +496,17 @@ contract PartyPool is PartyPoolBase, IPartyPool {
|
|||||||
/// @notice Compute fee and net amounts for a gross input (fee rounded up to favor the pool).
|
/// @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)
|
/// @return feeUint fee taken (uint) and netUint remaining for protocol use (uint)
|
||||||
function _computeFee(uint256 gross) internal view returns (uint256 feeUint, uint256 netUint) {
|
function _computeFee(uint256 gross) internal view returns (uint256 feeUint, uint256 netUint) {
|
||||||
if (swapFeePpm == 0) {
|
if (SWAP_FEE_PPM == 0) {
|
||||||
return (0, gross);
|
return (0, gross);
|
||||||
}
|
}
|
||||||
feeUint = _ceilFee(gross, swapFeePpm);
|
feeUint = _ceilFee(gross, SWAP_FEE_PPM);
|
||||||
netUint = gross - feeUint;
|
netUint = gross - feeUint;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @notice Convenience: return gross = net + fee(net) using ceiling for fee.
|
/// @notice Convenience: return gross = net + fee(net) using ceiling for fee.
|
||||||
function _addFee(uint256 netUint) internal view returns (uint256 gross) {
|
function _addFee(uint256 netUint) internal view returns (uint256 gross) {
|
||||||
if (swapFeePpm == 0) return netUint;
|
if (SWAP_FEE_PPM == 0) return netUint;
|
||||||
uint256 fee = _ceilFee(netUint, swapFeePpm);
|
uint256 fee = _ceilFee(netUint, SWAP_FEE_PPM);
|
||||||
return netUint + fee;
|
return netUint + fee;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -533,10 +538,10 @@ contract PartyPool is PartyPoolBase, IPartyPool {
|
|||||||
inputTokenIndex,
|
inputTokenIndex,
|
||||||
maxAmountIn,
|
maxAmountIn,
|
||||||
deadline,
|
deadline,
|
||||||
swapFeePpm
|
SWAP_FEE_PPM
|
||||||
);
|
);
|
||||||
|
|
||||||
bytes memory result = Address.functionDelegateCall(swapMintImpl, data);
|
bytes memory result = Address.functionDelegateCall(address(SWAP_MINT_IMPL), data);
|
||||||
return abi.decode(result, (uint256));
|
return abi.decode(result, (uint256));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -562,10 +567,10 @@ contract PartyPool is PartyPoolBase, IPartyPool {
|
|||||||
lpAmount,
|
lpAmount,
|
||||||
inputTokenIndex,
|
inputTokenIndex,
|
||||||
deadline,
|
deadline,
|
||||||
swapFeePpm
|
SWAP_FEE_PPM
|
||||||
);
|
);
|
||||||
|
|
||||||
bytes memory result = Address.functionDelegateCall(swapMintImpl, data);
|
bytes memory result = Address.functionDelegateCall(address(SWAP_MINT_IMPL), data);
|
||||||
return abi.decode(result, (uint256));
|
return abi.decode(result, (uint256));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -577,7 +582,7 @@ contract PartyPool is PartyPoolBase, IPartyPool {
|
|||||||
for (uint256 i = 0; i < tokens.length; i++) {
|
for (uint256 i = 0; i < tokens.length; i++) {
|
||||||
uint256 amount = loanAmounts[i];
|
uint256 amount = loanAmounts[i];
|
||||||
if (amount > 0) {
|
if (amount > 0) {
|
||||||
repaymentAmounts[i] = amount + _ceilFee(amount, flashFeePpm);
|
repaymentAmounts[i] = amount + _ceilFee(amount, FLASH_FEE_PPM);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -617,7 +622,7 @@ contract PartyPool is PartyPoolBase, IPartyPool {
|
|||||||
hasNonZeroAmount = true;
|
hasNonZeroAmount = true;
|
||||||
|
|
||||||
// Calculate repayment amount with fee (ceiling)
|
// Calculate repayment amount with fee (ceiling)
|
||||||
repaymentAmounts[i] = amount + _ceilFee(amount, flashFeePpm);
|
repaymentAmounts[i] = amount + _ceilFee(amount, FLASH_FEE_PPM);
|
||||||
|
|
||||||
// Record initial balance
|
// Record initial balance
|
||||||
initialBalances[i] = IERC20(tokens[i]).balanceOf(address(this));
|
initialBalances[i] = IERC20(tokens[i]).balanceOf(address(this));
|
||||||
@@ -640,7 +645,7 @@ contract PartyPool is PartyPoolBase, IPartyPool {
|
|||||||
|
|
||||||
// Verify repayment: current balance must be at least (initial balance + fee)
|
// Verify repayment: current balance must be at least (initial balance + fee)
|
||||||
require(
|
require(
|
||||||
currentBalance >= initialBalances[i] + _ceilFee(amounts[i], flashFeePpm),
|
currentBalance >= initialBalances[i] + _ceilFee(amounts[i], FLASH_FEE_PPM),
|
||||||
"flash: repayment failed"
|
"flash: repayment failed"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// SPDX-License-Identifier: UNLICENSED
|
// SPDX-License-Identifier: UNLICENSED
|
||||||
pragma solidity ^0.8.30;
|
pragma solidity ^0.8.30;
|
||||||
|
/* solhint-disable erc20-unchecked-transfer */
|
||||||
|
|
||||||
import "forge-std/Test.sol";
|
import "forge-std/Test.sol";
|
||||||
import "@abdk/ABDKMath64x64.sol";
|
import "@abdk/ABDKMath64x64.sol";
|
||||||
@@ -129,22 +130,23 @@ contract TestERC20 is ERC20 {
|
|||||||
/// @notice Gas testing contract for PartyPool - contains all gas measurement tests
|
/// @notice Gas testing contract for PartyPool - contains all gas measurement tests
|
||||||
contract GasTest is Test {
|
contract GasTest is Test {
|
||||||
using ABDKMath64x64 for int128;
|
using ABDKMath64x64 for int128;
|
||||||
|
using SafeERC20 for TestERC20;
|
||||||
|
|
||||||
PartyPlanner planner;
|
PartyPlanner internal planner;
|
||||||
PartyPool pool2;
|
PartyPool internal pool2;
|
||||||
PartyPool pool10;
|
PartyPool internal pool10;
|
||||||
PartyPool pool20;
|
PartyPool internal pool20;
|
||||||
PartyPool pool50;
|
PartyPool internal pool50;
|
||||||
|
|
||||||
address alice;
|
address internal alice;
|
||||||
address bob;
|
address internal bob;
|
||||||
|
|
||||||
// Common parameters
|
// Common parameters
|
||||||
int128 tradeFrac;
|
int128 internal tradeFrac;
|
||||||
int128 targetSlippage;
|
int128 internal targetSlippage;
|
||||||
|
|
||||||
uint256 constant INIT_BAL = 1_000_000; // initial token units for each token (internal==amount when base==1)
|
uint256 constant internal INIT_BAL = 1_000_000; // initial token units for each token (internal==amount when base==1)
|
||||||
uint256 constant BASE = 1; // use base=1 so internal amounts correspond to raw integers (Q64.64 units)
|
uint256 constant internal BASE = 1; // use base=1 so internal amounts correspond to raw integers (Q64.64 units)
|
||||||
|
|
||||||
/// @notice Helper function to create a pool with the specified number of tokens
|
/// @notice Helper function to create a pool with the specified number of tokens
|
||||||
function createPool(uint256 numTokens) internal returns (PartyPool) {
|
function createPool(uint256 numTokens) internal returns (PartyPool) {
|
||||||
|
|||||||
@@ -711,7 +711,7 @@ contract LMSRStabilizedTest is Test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Path 1: Direct swap from asset 0 to asset 2
|
// Path 1: Direct swap from asset 0 to asset 2
|
||||||
(int128 directAmountIn, int128 directAmountOut) = s.swapAmountsForExactInput(0, 2, directSwapAmount, 0);
|
(/*int128 directAmountIn*/, int128 directAmountOut) = s.swapAmountsForExactInput(0, 2, directSwapAmount, 0);
|
||||||
|
|
||||||
// Restore original state for second path
|
// Restore original state for second path
|
||||||
_updateCachedQInternal(backupQ);
|
_updateCachedQInternal(backupQ);
|
||||||
@@ -724,7 +724,7 @@ contract LMSRStabilizedTest is Test {
|
|||||||
s.qInternal[1] = s.qInternal[1].add(indirectAmountOut1);
|
s.qInternal[1] = s.qInternal[1].add(indirectAmountOut1);
|
||||||
|
|
||||||
// Second swap: asset 1 -> asset 2
|
// Second swap: asset 1 -> asset 2
|
||||||
(int128 indirectAmountIn2, int128 indirectAmountOut2) = s.swapAmountsForExactInput(1, 2, indirectAmountOut1, 0);
|
(/*int128 indirectAmountIn2*/, int128 indirectAmountOut2) = s.swapAmountsForExactInput(1, 2, indirectAmountOut1, 0);
|
||||||
|
|
||||||
// The path independence property isn't perfect due to discrete swap mechanics,
|
// The path independence property isn't perfect due to discrete swap mechanics,
|
||||||
// but the difference should be within reasonable bounds
|
// but the difference should be within reasonable bounds
|
||||||
@@ -765,7 +765,7 @@ contract LMSRStabilizedTest is Test {
|
|||||||
s.qInternal[1] = s.qInternal[1].add(amountOut1);
|
s.qInternal[1] = s.qInternal[1].add(amountOut1);
|
||||||
|
|
||||||
// Step 2: Swap back asset 1 -> asset 0
|
// Step 2: Swap back asset 1 -> asset 0
|
||||||
(int128 amountIn2, int128 amountOut2) = s.swapAmountsForExactInput(1, 0, amountOut1, 0);
|
(/*int128 amountIn2*/, int128 amountOut2) = s.swapAmountsForExactInput(1, 0, amountOut1, 0);
|
||||||
|
|
||||||
// Calculate round-trip slippage: (initial amount - final amount) / initial amount
|
// Calculate round-trip slippage: (initial amount - final amount) / initial amount
|
||||||
int128 roundTripSlippage = (amountIn1.sub(amountOut2)).div(amountIn1);
|
int128 roundTripSlippage = (amountIn1.sub(amountOut2)).div(amountIn1);
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ pragma solidity ^0.8.30;
|
|||||||
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
||||||
|
|
||||||
contract MockERC20 is ERC20 {
|
contract MockERC20 is ERC20 {
|
||||||
uint8 private immutable _decimals;
|
uint8 private immutable DECIMALS;
|
||||||
|
|
||||||
constructor(string memory name, string memory symbol, uint8 decimals_) ERC20(name, symbol) {_decimals = decimals_;}
|
constructor(string memory name, string memory symbol, uint8 decimals_) ERC20(name, symbol) {DECIMALS = decimals_;}
|
||||||
|
|
||||||
function decimals() public view virtual override returns (uint8) {return _decimals;}
|
function decimals() public view virtual override returns (uint8) {return DECIMALS;}
|
||||||
function mint(address account, uint256 amount) external {_mint(account, amount);}
|
function mint(address account, uint256 amount) external {_mint(account, amount);}
|
||||||
function burn(address account, uint256 amount) external {_burn(account, amount);}
|
function burn(address account, uint256 amount) external {_burn(account, amount);}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
// SPDX-License-Identifier: UNLICENSED
|
// SPDX-License-Identifier: UNLICENSED
|
||||||
|
/* solhint-disable */
|
||||||
pragma solidity ^0.8.30;
|
pragma solidity ^0.8.30;
|
||||||
|
|
||||||
import "forge-std/Test.sol";
|
import "forge-std/Test.sol";
|
||||||
@@ -1389,3 +1390,4 @@ contract PartyPoolTest is Test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
/* solhint-enable */
|
||||||
|
|||||||
Reference in New Issue
Block a user