AMM
什么是自动做市商 (AMM)
自动做市商(Automated Market Maker,AMM)是一种去中心化交易机制,它使用数学公式来定价资产,而不是传统的订单簿模式。JAMM DEX 采用恒定乘积公式(Constant Product Formula)作为其核心定价机制。
恒定乘积公式
基本原理
JAMM DEX 使用恒定乘积公式:x × y = k
其中:
x
= 代币A的储备量y
= 代币B的储备量k
= 恒定值(乘积)
这个公式确保了流动性池中两种代币的乘积始终保持恒定(除了交易费用和流动性变化的影响)。
代码实现
在 JAMMPair.sol
合约的 swap
函数中,这个原理通过以下方式得到验证:
// 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"
);
价格发现机制
价格计算
在AMM系统中,代币的价格由储备量比例决定:
代币A的价格 = 储备量B / 储备量A
价格影响
当用户进行交易时,会改变储备量比例,从而影响价格。交易量越大,价格影响越明显。
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;
}
滑点概念
什么是滑点
滑点是指预期价格与实际执行价格之间的差异。在AMM系统中,滑点主要由以下因素造成:
价格影响: 大额交易改变了储备量比例。
市场波动: 在交易确认期间,池中价格可能因其他交易而发生变化。
滑点保护
JAMM DEX 通过在 JAMMRouter
合约中检查用户可接受的最小输出来提供滑点保护:
// 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"
);
套利机制
套利的作用
套利者通过在不同市场之间进行交易来获利,同时帮助:
保持JAMM DEX的价格与外部市场一致。
为交易者提供更准确的价格。
增加交易量,从而为流动性提供者创造更多费用收入。
套利示例
如果JAMM DEX上的JU/USDC价格低于其他交易所:
套利者在JAMM DEX上买入JU。
在其他交易所卖出JU。
这个过程会推高JAMM DEX上的JU价格,直到价格与外部市场趋于一致。
流动性提供
流动性的重要性
流动性是AMM系统的核心:
高流动性: 较小的价格影响,更好的交易体验。
低流动性: 较大的价格影响,可能导致高滑点。
流动性提供者的角色
流动性提供者(LP)通过向池子中存入等值的代币对来提供流动性,并获得LP代币作为回报。JAMMPair.sol
中的 mint
函数处理此逻辑:
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)是指当池内代币价格发生变化时,流动性提供者(LP)撤出资金时所持资产的价值低于简单持有原始代币(HODL)的价值。价格偏离越大,无常损失越大。
计算原理
当代币价格发生变化时:
AMM会自动重新平衡储备量以维持
x*y=k
的关系。LP在池子中的份额保持不变,但其份额所代表的两种代币的数量会发生变化。
缓解措施
JAMM DEX通过以下方式帮助缓解无常损失:
交易费用收入: LP获得交易费用作为补偿,这可能抵消甚至超过无常损失。
多级费率: 为高波动性资产设置更高的费率,以提供更多费用收入。
价格预言机
累积价格机制
JAMM DEX实现了时间加权平均价格(TWAP)预言机。JAMMPair.sol
合约在每个区块的第一次交易中累积价格:
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;
}
// ...
}
预言机的用途
外部合约集成: 其他DeFi协议可以查询历史平均价格,用于借贷、衍生品等。
套利检测: 帮助识别价格偏差机会。
风险管理: 评估资产的价格波动风险。
最小流动性锁定
永久锁定机制
为了防止流动性池被完全移除而导致无法计算价格,JAMM DEX在第一次添加流动性时会永久锁定最小数量的LP代币到零地址。
// 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
}
这确保了:
池子永远不会完全清空。
价格计算始终有效。
防止某些类型的恶意操纵。
Last updated