AMM Basics
、
What is an Automated Market Maker (AMM)?
An Automated Market Maker (AMM) is a type of decentralized exchange (DEX) protocol that relies on a mathematical formula to price assets. Instead of using a traditional order book, AMMs use liquidity pools. JAMM DEX uses the Constant Product Formula as its core pricing mechanism.
The Constant Product Formula
The Basic Principle
JAMM DEX uses the constant product formula: x * y = k
Where:
x
= the reserve of token Ay
= the reserve of token Bk
= a constant product
This formula ensures that the product of the reserves of the two tokens in a liquidity pool remains constant (disregarding the impact of trading fees and liquidity changes).
Code Implementation
In the JAMMPair.sol
contract, this principle is enforced in the swap
function:
// balance{0,1}Adjusted are the balances after the swap, accounting for the fee
uint balance0Adjusted = (balance0 * 10000 - (amount0In * fee * 4) / 5);
uint balance1Adjusted = (balance1 * 10000 - (amount1In * fee * 4) / 5);
// check that the product of the new balances is not less than the product of the old reserves
require(
balance0Adjusted * balance1Adjusted >=
uint(_reserve0) * uint(_reserve1) * (10000 ** 2),
"JAMM: K"
);
Price Discovery Mechanism
Price Calculation
In an AMM system, the price of a token is determined by the ratio of the reserves:
Price of Token A = Reserve of Token B / Reserve of Token A
Price Impact
When a user trades, the ratio of the reserves changes, which in turn affects the price. The larger the trade, the more significant the price impact.
The price calculation function in JAMMLibrary.sol
:
function getAmountOut(
uint amountIn,
uint reserveIn,
uint reserveOut,
uint24 fee
) internal pure returns (uint amountOut) {
require(amountIn > 0, "JAMMLibrary: INSUFFICIENT_INPUT_AMOUNT");
require(reserveIn > 0 && reserveOut > 0, "JAMMLibrary: INSUFFICIENT_LIQUIDITY");
// calculate output amount with fee
uint amountInWithFee = amountIn * (10000 - fee);
uint numerator = amountInWithFee * reserveOut;
uint denominator = reserveIn * 10000 + amountInWithFee;
amountOut = numerator / denominator;
}
The Concept of Slippage
What is Slippage?
Slippage is the difference between the expected price of a trade and the price at which the trade is executed. In an AMM system, slippage is mainly caused by:
Price Impact: A large trade changes the reserve ratio.
Market Volatility: The pool's price can change due to other trades during the transaction confirmation time.
Slippage Protection
JAMM DEX provides slippage protection by checking the minimum acceptable output specified by the user in the JAMMRouter
contract:
// check that the received amount is not less than the minimum amount specified by the user
require(
amounts[amounts.length - 1] >= amountOutMin,
"JAMMRouter: INSUFFICIENT_OUTPUT_AMOUNT"
);
Arbitrage Mechanism
The Role of Arbitrage
Arbitrageurs profit from price differences between markets, which helps to:
Keep JAMM DEX's prices consistent with external markets.
Provide more accurate prices for traders.
Increase trading volume, generating more fee income for liquidity providers.
Arbitrage Example
If the JU/USDC price on JAMM DEX is lower than on other exchanges:
An arbitrageur buys JU on JAMM DEX.
They sell JU on another exchange.
This process drives up the price of JU on JAMM DEX until it aligns with the external market price.
Liquidity Provision
The Importance of Liquidity
Liquidity is the core of an AMM system:
High Liquidity: Results in smaller price impact and a better trading experience.
Low Liquidity: Leads to larger price impact and potentially high slippage.
The Role of Liquidity Providers
Liquidity Providers (LPs) supply liquidity by depositing an equivalent value of two tokens into a pool, receiving LP tokens in return. The mint
function in JAMMPair.sol
handles this logic:
function mint(address to) external lock returns (uint liquidity) {
(uint112 _reserve0, uint112 _reserve1, ) = getReserves();
uint balance0 = IERC20(token0).balanceOf(address(this));
uint balance1 = IERC20(token1).balanceOf(address(this));
uint amount0 = balance0 - _reserve0;
uint amount1 = balance1 - _reserve1;
bool feeOn = _mintFee(_reserve0, _reserve1);
uint _totalSupply = totalSupply;
// calculate liquidity to mint
if (_totalSupply == 0) {
liquidity = Math.sqrt(amount0 * amount1) - MINIMUM_LIQUIDITY;
_mint(address(0), MINIMUM_LIQUIDITY); // permanently lock the first MINIMUM_LIQUIDITY tokens
} else {
liquidity = Math.min(
(amount0 * _totalSupply) / _reserve0,
(amount1 * _totalSupply) / _reserve1
);
}
require(liquidity > 0, "JAMM: INSUFFICIENT_LIQUIDITY_MINTED");
_mint(to, liquidity);
_update(balance0, balance1, _reserve0, _reserve1);
if (feeOn) kLast = uint(reserve0) * uint(reserve1);
emit Mint(msg.sender, amount0, amount1);
}
Impermanent Loss
Definition
Impermanent Loss (IL) is the difference in value between holding assets in a liquidity pool and simply holding them in a wallet (HODLing). The greater the price divergence, the larger the impermanent loss.
How it Works
When the price of tokens in the pool changes:
The AMM automatically rebalances the reserves to maintain the
x*y=k
constant.An LP's share of the pool remains the same, but the quantities of the two tokens represented by that share change.
Mitigation Measures
JAMM DEX helps mitigate impermanent loss through:
Trading Fee Income: LPs earn trading fees as compensation, which can offset or even exceed the impermanent loss.
Multi-Tier Fees: Higher fees can be set for more volatile assets to provide greater fee income.
Price Oracle
Price Accumulation Mechanism
JAMM DEX implements a Time-Weighted Average Price (TWAP) oracle. The JAMMPair.sol
contract accumulates prices during the first trade of each block:
function _update(
uint balance0,
uint balance1,
uint112 _reserve0,
uint112 _reserve1
) private {
// ...
uint32 timeElapsed = blockTimestamp - blockTimestampLast;
if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) {
// accumulate prices
price0CumulativeLast += uint(UQ112x112.encode(_reserve1).uqdiv(_reserve0)) * timeElapsed;
price1CumulativeLast += uint(UQ112x112.encode(_reserve0).uqdiv(_reserve1)) * timeElapsed;
}
// ...
}
Use Cases for the Oracle
External Contract Integration: Other DeFi protocols can query historical average prices for lending, derivatives, etc.
Arbitrage Detection: Helps identify price deviation opportunities.
Risk Management: Assess the price volatility risk of assets.
Minimum Liquidity Lock
Permanent Lock Mechanism
To prevent a liquidity pool from being completely drained, which would make price calculations impossible, JAMM DEX permanently locks a minimum amount of LP tokens to the zero address during the first liquidity provision.
// From JAMMPair.sol
uint public constant MINIMUM_LIQUIDITY = 10**3;
// Inside the mint function
if (_totalSupply == 0) {
liquidity = Math.sqrt(amount0 * amount1) - MINIMUM_LIQUIDITY;
_mint(address(0), MINIMUM_LIQUIDITY); // permanently lock the first tokens
}
This ensures that:
The pool is never completely empty.
Price calculations are always valid.
Certain types of malicious manipulation are prevented.
Summary
The AMM mechanism provides an elegant solution for decentralized trading:
No Order Book: Prices are determined automatically by a mathematical formula.
Continuous Liquidity: Trades can always be executed as long as there are reserves in the pool.
Decentralization: No need for centralized market makers.
Transparency: All logic is executed publicly on the blockchain.
Understanding these fundamental concepts is crucial for using JAMM DEX effectively. In the next section, we will explore Liquidity Pools in more detail.