Skip to main content

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

PropertyValue
LicenseBUSL-1.1
Solidity^0.8.25
InheritanceOwnable, 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:

NameTypeDescription
_assetAIFHERC20First token address
_assetBIFHERC20Second token address
_swapFeeuint24Swap fee in basis points
initialOwneraddressContract 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:

NameTypeDescription
amountAInInEuint64Encrypted amount of token A
amountBInInEuint64Encrypted 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:

NameTypeDescription
amountAInInEuint64Encrypted amount of token A
amountBInInEuint64Encrypted 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:

NameTypeDescription
tokenIduint256Position 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:

NameTypeDescription
amountInEncInEuint64Encrypted input amount
recipientaddressAddress to receive output
hookContextbytesData passed to hooks

swapExactBForA

Swap token B for token A.

function swapExactBForA(
InEuint64 memory amountInEnc,
address recipient,
bytes calldata hookContext
) external ensureInitialized nonReentrant

Parameters:

NameTypeDescription
amountInEncInEuint64Encrypted input amount
recipientaddressAddress to receive output
hookContextbytesData passed to hooks

Fee Management

claimFees

Claim accumulated LP fees for a position.

function claimFees(
uint256 tokenId,
address to
) external nonReentrant

Parameters:

NameTypeDescription
tokenIduint256Position NFT ID
toaddressRecipient 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:

  • bps must 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);