跳转到主要内容

V3 Factory 与 Pool

V3 Factory

V3 Factory 负责创建和索引 V3 流动性池。与 V2 不同,相同的代币对可以拥有多个不同费率等级的流动性池

核心函数

getPool

返回指定代币对和费率等级的流动性池地址。
function getPool(
    address tokenA,
    address tokenB,
    uint24 fee
) external view returns (address pool);
参数描述
tokenA, tokenB代币地址(顺序无关)
fee费率等级:100500300010000
如果流动性池不存在则返回零地址。

ABI

[
  "function getPool(address tokenA, address tokenB, uint24 fee) view returns (address pool)"
]

V3 Pool

每个 V3 Pool 合约管理特定代币对和费率等级的集中流动性仓位和兑换执行。

核心函数

slot0

返回当前流动性池状态。
function slot0() external view returns (
    uint160 sqrtPriceX96,
    int24 tick,
    uint16 observationIndex,
    uint16 observationCardinality,
    uint16 observationCardinalityNext,
    uint8 feeProtocol,
    bool unlocked
);
返回值描述
sqrtPriceX96当前价格,表示为 sqrt(price) * 2^96
tick当前 tick 索引
feeProtocol协议费率设置
unlocked重入保护状态

liquidity

返回当前活跃的(在范围内的)总流动性。
function liquidity() external view returns (uint128);

fee / tickSpacing

function fee() external view returns (uint24);
function tickSpacing() external view returns (int24);

token0 / token1

function token0() external view returns (address);
function token1() external view returns (address);

ABI

[
  "function slot0() view returns (uint160 sqrtPriceX96, int24 tick, uint16 observationIndex, uint16 observationCardinality, uint16 observationCardinalityNext, uint8 feeProtocol, bool unlocked)",
  "function liquidity() view returns (uint128)",
  "function fee() view returns (uint24)",
  "function tickSpacing() view returns (int24)",
  "function token0() view returns (address)",
  "function token1() view returns (address)"
]

示例:读取池价格

const FACTORY_ABI = [
  "function getPool(address, address, uint24) view returns (address)"
];
const POOL_ABI = [
  "function slot0() view returns (uint160, int24, uint16, uint16, uint16, uint8, bool)",
  "function token0() view returns (address)",
  "function token1() view returns (address)",
  "function fee() view returns (uint24)",
  "function liquidity() view returns (uint128)"
];

const factory = new ethers.Contract(V3_FACTORY, FACTORY_ABI, provider);
const poolAddress = await factory.getPool(tokenA, tokenB, 3000); // 0.3% 费率

if (poolAddress === ethers.ZeroAddress) {
  console.log("Pool does not exist for this fee tier");
} else {
  const pool = new ethers.Contract(poolAddress, POOL_ABI, provider);
  const [sqrtPriceX96, tick] = await pool.slot0();

  // 将 sqrtPriceX96 转换为价格
  const price = (Number(sqrtPriceX96) / 2 ** 96) ** 2;
  console.log(`Current price (token1/token0): ${price}`);
  console.log(`Current tick: ${tick}`);
}

价格转换

流动性池以 sqrtPriceX96 格式存储价格。转换方法如下:
// sqrtPriceX96 → 价格(token1 每 token0)
const price = (sqrtPriceX96 / 2n ** 96n) ** 2n; // BigInt 写法

// 针对不同代币精度的调整
const adjustedPrice = price * 10n ** BigInt(decimals0 - decimals1);
如需精确计算,请使用 @uniswap/v3-sdk 库或参阅 Ticks 和范围 中的公式。