PrivacyPool
The core AMM contract implementing a constant product market maker with encrypted reserves.
Overview
PrivacyPool is the main liquidity pool contract in Lunarys Protocol. It manages:
- Encrypted reserves for a token pair
- Swap execution with FHE arithmetic
- Fee collection and distribution
- Liquidity position management
Contract Details
| Property | Value |
|---|---|
| License | BUSL-1.1 |
| Solidity | ^0.8.25 |
| Inheritance | Ownable, ReentrancyGuard |
State Variables
Immutable
IFHERC20 public immutable assetA; // First token of the pair
IFHERC20 public immutable assetB; // Second token of the pair
Encrypted State
euint64 private reserveA; // Encrypted reserve of token A
euint64 private reserveB; // Encrypted reserve of token B
euint32 private swapFeeEncrypted; // Fee percentage (BPS)
euint64 private feeVaultA; // Accumulated LP fees in token A
euint64 private feeVaultB; // Accumulated LP fees in token B
euint64 private protocolVaultA; // Protocol fees in token A
euint64 private protocolVaultB; // Protocol fees in token B
euint64 private totalLiquidity; // Total liquidity shares
euint128 private feeGrowthA_RAY; // Cumulative fee growth for A
euint128 private feeGrowthB_RAY; // Cumulative fee growth for B
Configuration
uint32 public constant MAX_PROTOCOL_FEE_BPS = 10_000; // 1% max protocol fee
uint256 private constant BPS_DENOMINATOR = 1_000_000;
bool public reservesInitialized;
address public protocolFeeRecipient;
IPositionNFT public positionNFT;
Constructor
constructor(
IFHERC20 _assetA,
IFHERC20 _assetB,
uint24 _swapFee,
address initialOwner
)
Parameters:
| Name | Type | Description |
|---|---|---|
| _assetA | IFHERC20 | First token address |
| _assetB | IFHERC20 | Second token address |
| _swapFee | uint24 | Swap fee in basis points |
| initialOwner | address | Contract owner |
Functions
Liquidity Management
bootstrap
Initialize the pool with initial liquidity. Can only be called once by the owner.
function bootstrap(
InEuint64 memory amountAIn,
InEuint64 memory amountBIn
) external onlyOwner nonReentrant
Parameters:
| Name | Type | Description |
|---|---|---|
| amountAIn | InEuint64 | Encrypted amount of token A |
| amountBIn | InEuint64 | Encrypted amount of token B |
Requirements:
- Pool must not be initialized
- Caller must be owner
- Both amounts must be valid encrypted values
contributeLiquidity
Add liquidity to an initialized pool.
function contributeLiquidity(
InEuint64 memory amountAIn,
InEuint64 memory amountBIn
) external ensureInitialized nonReentrant
Parameters:
| Name | Type | Description |
|---|---|---|
| amountAIn | InEuint64 | Encrypted amount of token A |
| amountBIn | InEuint64 | Encrypted amount of token B |
Effects:
- Pulls tokens from caller
- Updates reserves
- Mints PositionNFT to caller
removeLiquidity
Remove liquidity by burning a position NFT.
function removeLiquidity(
uint256 tokenId
) external ensureInitialized nonReentrant
Parameters:
| Name | Type | Description |
|---|---|---|
| tokenId | uint256 | Position NFT ID |
Requirements:
- Caller must own the position NFT
- Position must match this pool's token pair
Swaps
swapExactAForB
Swap token A for token B.
function swapExactAForB(
InEuint64 memory amountInEnc,
address recipient,
bytes calldata hookContext
) external ensureInitialized nonReentrant
Parameters:
| Name | Type | Description |
|---|---|---|
| amountInEnc | InEuint64 | Encrypted input amount |
| recipient | address | Address to receive output |
| hookContext | bytes | Data passed to hooks |
swapExactBForA
Swap token B for token A.
function swapExactBForA(
InEuint64 memory amountInEnc,
address recipient,
bytes calldata hookContext
) external ensureInitialized nonReentrant
Parameters:
| Name | Type | Description |
|---|---|---|
| amountInEnc | InEuint64 | Encrypted input amount |
| recipient | address | Address to receive output |
| hookContext | bytes | Data passed to hooks |
Fee Management
claimFees
Claim accumulated LP fees for a position.
function claimFees(
uint256 tokenId,
address to
) external nonReentrant
Parameters:
| Name | Type | Description |
|---|---|---|
| tokenId | uint256 | Position NFT ID |
| to | address | Recipient of fees |
claimProtocolFees
Claim protocol fees (owner only).
function claimProtocolFees(
address to
) external onlyOwner nonReentrant
Configuration
setPositionNFT
Set the position NFT contract address.
function setPositionNFT(
IPositionNFT newPositionNFT
) external onlyOwner
setProtocolFee
Configure protocol fee parameters.
function setProtocolFee(
uint32 bps,
address recipient
) external onlyOwner
Requirements:
bpsmust not exceed MAX_PROTOCOL_FEE_BPS (10,000 equals 1%)
configureHooks
Set swap and liquidity hooks.
function configureHooks(
IPrivacyPoolSwapHook swapHook,
bytes calldata swapData,
IPrivacyPoolLiquidityHook liquidityHook,
bytes calldata liquidityData
) external onlyOwner
View Functions
latestReserveHandles
Get encrypted reserve handles.
function latestReserveHandles() external view returns (
uint256 handleA,
uint256 handleB
)
snapshotFees
Get cumulative fee handles.
function snapshotFees() external view returns (
uint256 feesAHandle,
uint256 feesBHandle
)
Events
event SwapExecuted(
address indexed sender,
address indexed recipient,
bool aForB,
uint256 amountInHandle,
uint256 amountOutHandle
);
event LiquiditySeeded(address indexed provider);
event LiquidityAdjusted(address indexed caller, bool add);
event FeesClaimed(
uint256 indexed tokenId,
address indexed to,
uint256 amountAHandle,
uint256 amountBHandle
);
event ProtocolFeesClaimed(
address indexed to,
uint256 amountAHandle,
uint256 amountBHandle
);
Errors
error InvalidRecipient();
error PositionNFTNotConfigured();
error NotPositionOwner(uint256 tokenId, address caller);
error PositionTokenMismatch(uint256 tokenId);
error ZeroLiquidity();
AMM Formula
The swap calculation follows the constant product formula:
k = reserveIn * reserveOut
newReserveIn = reserveIn + effectiveAmountIn
newReserveOut = k / newReserveIn
amountOut = reserveOut - newReserveOut
Where effectiveAmountIn = amountIn - fees
Fee Calculation
Fees are split between LPs and protocol:
totalFee = amountIn * swapFee / BPS_DENOMINATOR
protocolFee = min(amountIn * protocolFeeBps / BPS_DENOMINATOR, totalFee)
lpFee = totalFee - protocolFee
Usage Example
// Initialize pool
pool.bootstrap(encryptedAmountA, encryptedAmountB);
// Execute swap
pool.swapExactAForB(encryptedSwapAmount, recipient, "");
// Add liquidity
pool.contributeLiquidity(encryptedAmountA, encryptedAmountB);
// Remove liquidity
pool.removeLiquidity(positionNftId);
// Claim fees
pool.claimFees(positionNftId, feeRecipient);