Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 19 additions & 4 deletions contracts/FpmmAMM.sol
Original file line number Diff line number Diff line change
Expand Up @@ -410,13 +410,25 @@ contract FpmmAMM is ReentrancyGuard, IERC1155Receiver {
* @dev First LP sets initial state, subsequent LPs get proportional shares
* Caller must have approved this contract to spend collateral
*/
/**
* @notice Add liquidity to an FPMM market
* @param marketId The market to provide liquidity to
* @param collateralAmount Amount of collateral to deposit
* @param minLpSharesOut Minimum LP shares to receive (slippage protection)
* @param receiver The address to receive the LP shares
* @return lpSharesOut LP shares minted to provider
* @dev First LP sets initial state, subsequent LPs get proportional shares
* Caller must have approved this contract to spend collateral
*/
function addLiquidity(
bytes32 marketId,
uint256 collateralAmount,
uint256 minLpSharesOut
uint256 minLpSharesOut,
address receiver
) external nonReentrant returns (uint256 lpSharesOut) {
if (!isRegistered[marketId]) revert MarketNotRegistered();
if (collateralAmount == 0) revert ZeroAmount();
if (receiver == address(0)) revert ZeroAmount(); // Reuse ZeroAmount for ZeroAddress to save space/gas? Or separate error? stick to standard checks if available or just logic.
Comment thread
0xJonHoldsCrypto marked this conversation as resolved.

// Verify market is still open
if (!IMarketCore(marketCore).isMarketOpen(marketId)) {
Expand All @@ -426,7 +438,8 @@ contract FpmmAMM is ReentrancyGuard, IERC1155Receiver {
FpmmMarketConfig storage config = _configs[marketId];
FpmmMarketState storage state = _states[marketId];

// Transfer collateral from LP to AMM
// Transfer collateral from LP (msg.sender) to AMM
// Note: msg.sender provides the collateral, receiver gets the shares
IERC20(config.collateralToken).safeTransferFrom(
msg.sender,
address(this),
Expand All @@ -447,9 +460,11 @@ contract FpmmAMM is ReentrancyGuard, IERC1155Receiver {
// Update state
state.collateralBalance += collateralAmount;
state.lpShareSupply += lpSharesOut;
lpShares[marketId][msg.sender] += lpSharesOut;
lpShares[marketId][receiver] += lpSharesOut; // Credit ONLY the receiver

emit LiquidityAdded(marketId, msg.sender, collateralAmount, lpSharesOut);
// Event should reflect the receiver as the provider of liquidity in terms of ownership
// But maybe we want to know who paid? Ideally receiver is what matters for "Who Added Liquidity" in UI if we use this event.
emit LiquidityAdded(marketId, receiver, collateralAmount, lpSharesOut);
}

/**
Expand Down
9 changes: 4 additions & 5 deletions contracts/SimpleRouter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -383,10 +383,8 @@ contract SimpleRouter is ReentrancyGuard {
_ensureApproval(collateralToken, fpmmAMM, collateralAmount);

// Add liquidity through FpmmAMM
lpShares = IFpmmAMM(fpmmAMM).addLiquidity(marketId, collateralAmount, minLpShares);

// Note: LP shares are tracked in FpmmAMM contract, credited to msg.sender
// The router facilitated the transaction but shares belong to the caller
// Pass msg.sender as the receiver so shares are credited to the USER, not the Router
lpShares = IFpmmAMM(fpmmAMM).addLiquidity(marketId, collateralAmount, minLpShares, msg.sender);

emit LiquidityProvided(marketId, msg.sender, collateralAmount, lpShares);
}
Expand Down Expand Up @@ -702,7 +700,8 @@ interface IFpmmAMM {
function addLiquidity(
bytes32 marketId,
uint256 collateralAmount,
uint256 minLpSharesOut
uint256 minLpSharesOut,
address receiver
) external returns (uint256);

function removeLiquidity(
Expand Down
56 changes: 40 additions & 16 deletions contracts/UniV3EthUsdTwapOracleAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -565,25 +565,49 @@ contract UniV3EthUsdTwapOracleAdapter is IOutcomeOracle {
uint256 price;
uint256 sqrtPrice = uint256(sqrtPriceX96);

// We want the price of Base Token in terms of Quote Token
// i.e., How many Quote Tokens for 1 Base Token?
// Formula: Price_QuoteDecimals = (QuoteRaw / BaseRaw) * 10^BaseDecimals

uint256 ratioX192; // R * 2^192

if (baseToken == token0) {
// Pool price is token1/token0, we want token0/token1 (invert)
// price = 1 / (sqrtPriceX96^2 / 2^192) = 2^192 / sqrtPriceX96^2
// Scale to quote decimals
price = (uint256(1) << 192) * (10 ** quoteDecimals) / (sqrtPrice * sqrtPrice);
// Adjust for base token decimals
if (baseDecimals > 0) {
price = price / (10 ** baseDecimals);
}
// Pool price (sqrtPrice) is token1/token0 (Quote/Base)
// This IS the ratio we want: QuoteRaw / BaseRaw
// ratio = sqrtPrice^2
ratioX192 = (sqrtPrice * sqrtPrice);
} else {
// Pool price is token1/token0, baseToken is token1, so price is correct direction
// price = sqrtPriceX96^2 / 2^192
price = (sqrtPrice * sqrtPrice) >> 192;
// Scale for decimals
price = price * (10 ** quoteDecimals);
if (baseDecimals > quoteDecimals) {
price = price / (10 ** (baseDecimals - quoteDecimals));
}
// Pool price is token1/token0 (Base/Quote)
// We want Quote/Base, so we invert
// ratio = 1 / sqrtPrice^2
// ratioX192 = 2^192 * 2^192 / sqrtPrice^2 = 2^384 / sqrtPrice^2
// To be safe with overflow, do: (2^192 / sqrtPrice)^2 ?
// Better: (2^192 / sqrtPrice) * (2^192 / sqrtPrice)
// 2^192 is ~6e57. sqrtPrice is ~4e24. Safe.
Comment thread
0xJonHoldsCrypto marked this conversation as resolved.

uint256 invSqrt = (uint256(1) << 192) / sqrtPrice;
ratioX192 = invSqrt * invSqrt;
}

// Now we have Ratio * 2^192
// We want Price = Ratio * 10^BaseDecimals

// Calculate 10^BaseDecimals
uint256 powerOfBase = 10 ** baseDecimals;

// Final Price = (RatioX192 * powerOfBase) >> 192
// To maintain precision, mul then shift

// Check for overflow before multiply
// ratioX192 can be up to ~2^192 (if ratio is 1)
// powerOfBase is 10^18 (~2^60)
// 192 + 60 = 252. Fits in 256.

// However, if ratio >> 1 (e.g. BTC/USD), ratioX192 >> 2^192.
// For WETH/USDC (ratio ~3e-9), ratioX192 is small (~2^163).
// 163 + 60 = 223. Safe.

price = (ratioX192 * powerOfBase) >> 192;

return price;
}
Expand Down