
剖析DeFi交易产品之UniswapV3:头寸管理合约
实现用户层面的流动性头寸管理的合约是 NonfungiblePositionManager 合约,其实现比较复杂,还继承了很多子合约,限于篇幅,我们无法全都一一讲解,就只能挑一些重点的来讲。
前面我们说过,UniswapV3 的 LP Token 其实是不可互换的 NFT,是 ERC721 Token。实际上,NonfungiblePositionManager 就继承了 ERC721,从代码上来看,继承了 ERC721Permit 抽象合约,所以,所有 LP Token(即头寸)都是在 NonfungiblePositionManager 合约里进行管理的。
我们主要看几个核心流程,包括创建并初始化流动性池、创建新头寸、为头寸增加流动性、减少流动性、提取手续费收益、销毁头寸。
创建并初始化流动性池
创建并初始化流动性池对应的函数为 createAndInitializePoolIfNecessary
,其代码实现是在所继承的抽象合约 PoolInitializer 里,实现代码如下:
function createAndInitializePoolIfNecessary(
address token0,
address token1,
uint24 fee,
uint160 sqrtPriceX96
) external payable override returns (address pool) {
require(token0 < token1);
// 从工厂合约查出pool地址
pool = IUniswapV3Factory(factory).getPool(token0, token1, fee);
if (pool == address(0)) { // pool合约未创建
pool = IUniswapV3Factory(factory).createPool(token0, token1, fee);
IUniswapV3Pool(pool).initialize(sqrtPriceX96);
} else {
(uint160 sqrtPriceX96Existing, , , , , , ) = IUniswapV3Pool(pool).slot0();
if (sqrtPriceX96Existing == 0) {
IUniswapV3Pool(pool).initialize(sqrtPriceX96);
}
}
}
传入参数为 token0、token1、fee 和初始根号价格 sqrtPriceX96。
实现现逻辑很简单,先根据 token0、token1、fee 这三个字段从工厂合约查询 pool 是否已经存在。如果不存在的话,则通过工厂合约的 createPool
函数创建池子,再通过 IUniswapV3Pool (即池子的合约)的 initialize 函数初始化池子的价格和 tick 等状态。如果从工厂合约查询到 pool 已经存在了,那就通过读取池子合约的 slot0
来获取到其当前价格,如果价格为 0 就表示还没初始化,所以再执行一次 initialize
。
创建头寸
createAndInitializePoolIfNecessary
只是完成了池子的创建和初始化状态,这时候的池子里还没有任何头寸和流动性的。下一步需要调用 NonfungiblePositionManager 合约的 mint
函数来创建头寸并添加流动性。先看其入参,mint
函数的入参是一个结构体 MintParams
。当一个函数要传的参数比较多的时候,把这些参数封装到一个结构体对象里是常用的做法。MintParams
具体的参数如下:
struct MintParams {
address token0; //组成池子的token0
address token1; //组成池子的token1
uint24 fee; //组成池子的费率
int24 tickLower; //价格区间的下限对应的tick序号
int24 tickUpper; //价格区间的上限对应的tick序号
uint256 amount0Desired; //要添加作为流动性的token0数量(预估值)
uint256 amount1Desired; //要添加作为流动性的token1数量(预估值)
uint256 amount0Min; //作为流动性的token0最小数量
uint256 amount1Min; //作为流动性的tokne1最小数量
address recipient; //接收头寸的地址
uint256 deadline; //过期时间
}
其中,amount0Desired
和 amount1Desired
其实是预估的数量。从用户端发起交易,到实际链上执行交易是存在时延的,这期间可能有其他用户也添加了流动性,所以最终成交时的数量可能会和 Desired 的值不一样。如果期间价格变化比较大,也会导致用户实际成交时的滑点很大,因此在前端页面上其实会有一个滑点设置来保护用户实际成交时不会超过设置的滑点值。amount0Min
和 amount1Min
就是根据设置的滑点值计算出来的。
过期时间则和 UniswapV2 时候的过期时间是一样用法,当实际执行交易时,如果已经过了这个 deadline 时间,就不会继续执行交易。
以下是 mint
函数的代码实现:
function mint(MintParams calldata params)
external
payable
override
checkDeadline(params.deadline) //检查是否过期
returns (
uint256 tokenId, //ERC721的tokenId
uint128 liquidity, //添加所得的流动性数额
uint256 amount0, //实际成交的amount0
uint256 amount1 //实际成交的amount1
)
{
IUniswapV3Pool pool;
//调用内部函数实现流动性的添加
(liquidity, amount0, amount1, pool) = addLiquidity(
AddLiquidityParams({
token0: params.token0,
token1: params.token1,
fee: params.fee,
recipient: address(this), //底层流动性的接收者是当前地址
tickLower: params.tickLower,
tickUpper: params.tickUpper,
amount0Desired: params.amount0Desired,
amount1Desired: params.amount1Desired,
amount0Min: params.amount0Min,
amount1Min: params.amount1Min
})
);
//铸造给用户一个ERC721 tokenId,作为当前流动性头寸的凭证,tokenId是递增的
_mint(params.recipient, (tokenId = _nextId++));
//计算出头寸的Key
bytes32 positionKey = PositionKey.compute(address(this), params.tickLower, params.tickUpper);
//从头寸数据里获取出最近手续费数据
(, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128, , ) = pool.positions(positionKey);
//获取poolId,和tokenId一样,也是递增的
uint80 poolId =
cachePoolKey(
address(pool),
PoolAddress.PoolKey({token0: params.token0, token1: params.token1, fee: params.fee})
);
//存储用户的头寸数据
_positions[tokenId] = Position({
nonce: 0,
operator: address(0),
poolId: poolId,
tickLower: params.tickLower,
tickUpper: params.tickUpper,
liquidity: liquidity,
feeGrowthInside0LastX128: feeGrowthInside0LastX128,
feeGrowthInside1LastX128: feeGrowthInside1LastX128,
tokensOwed0: 0,
tokensOwed1: 0
});
//发送事件
emit IncreaseLiquidity(tokenId, liquidity, amount0, amount1);
}
内部实现的第一步是调用了内部函数 addLiquidity
来添加流动性,该函数的代码实现是在继承的抽象合约 LiquidityManagement 里,代码实现如下:
function addLiquidity(AddLiquidityParams memory params)
internal
returns (
uint128 liquidity,
uint256 amount0,
uint256 amount1,
IUniswapV3Pool pool
)
{
PoolAddress.PoolKey memory poolKey =
PoolAddress.PoolKey({token0: params.token0, token1: params.token1, fee: params.fee});
//计算出pool地址,转为IUniswapV3Pool实例
pool = IUniswapV3Pool(PoolAddress.computeAddress(factory, poolKey));
{
//读取出当前价格
(uint160 sqrtPriceX96, , , , , , ) = pool.slot0();
//根据tick计算出对应的价格
uint160 sqrtRatioAX96 = TickMath.getSqrtRatioAtTick(params.tickLower);
uint160 sqrtRatioBX96 = TickMath.getSqrtRatioAtTick(params.tickUpper);
//计算出流动性数额
liquidity = LiquidityAmounts.getLiquidityForAmounts(
sqrtPriceX96,
sqrtRatioAX96,
sqrtRatioBX96,
params.amount0Desired,
params.amount1Desired
);
}
//调用底层池子的mint函数
(amount0, amount1) = pool.mint(
params.recipient, //这里的recipient是当前合约地址
params.tickLower,
params.tickUpper,
liquidity,
abi.encode(MintCallbackData({poolKey: poolKey, payer: msg.sender})) //回调数据
);
//检查是否超过滑点值
require(amount0 >= params.amount0Min && amount1 >= params.amount1Min, 'Price slippage check');
}
其中,pool 地址是通过 PoolAddress 库的 computeAddress
函数计算出来的。具体算法也很简单,其代码如下:
function computeAddress(address factory, PoolKey memory key) internal pure returns (address pool) {
require(key.token0 < key.token1);
pool = address(
uint256(
keccak256(
abi.encodePacked(
hex'ff',
factory,
keccak256(abi.encode(key.token0, key.token1, key.fee)),
POOL_INIT_CODE_HASH
)
)
)
);
}
这也是因为使用了 create2 的方式创建 pool 合约,所以才可以使用这种方式来计算合约地址。链下也可以使用同样方式来单独计算 pool 地址。
接着,用大括号括起来的代码段实现了计算 liquidity
,即当前加入该头寸中的流动性数量。该流动性的计算可分为三种情况:
-
当前价格小于价格区间下限时,流动性 L 的计算公式如下:
liquidity = amount0 * (sqrt(upper) * sqrt(lower)) / (sqrt(upper) - sqrt(lower))
-
当前价格大于价格区间上限时,则使用如下计算公式:
liquidity = amount1 / (sqrt(upper) - sqrt(lower))
-
当前价格处于价格区间内时,以上两个公式都计算,然后取最小值的那个。
之后,就调用 pool 合约的 mint
函数实现底层的添加流动性操作了,会返回实际成交的 amount0
和 amount1
。调用底层 mint
函数的时候,最后一个参数将 poolKey
和 payer
编码成 data
以供回调函数使用。
至此,LiquidityManagement 抽象合约里的 addLiquidity
函数逻辑就这些了。
回到 NonfungiblePositionManager 合约的 mint
函数,执行完 addLiquidity
函数之后,调用了 _mint
函数,如下:
_mint(params.recipient, (tokenId = _nextId++));
这其实是 ERC721 实现的内部函数,也是铸造新 NFT 的函数,第一个参数是接收该 NFT 的地址,第二个参数是该 NFT 的 tokenId。每个 NFT 就是一个头寸。这里,tokenId = _nextId++
,即 tokenId
会被赋值为当前 _nextId
的值,然后 _nextId
自增 1。_nextId
是 NonfungiblePositionManager 合约的私有状态变量,默认从 1 开始。即是说,代表头寸的 tokenId 其实是根据创建顺序进行递增的。
之后,再计算出 poolId 等数据,创建一个 Position 对象实例,以 tokenId 为 key,存储到 _positions
里。_positons
是一个 mapping 类型的私有状态变量,用于存储所有头寸对象。
最后,发送 IncreaseLiquidity
事件。至此,mint
函数的逻辑就完成了。
在这个 mint
函数里,并没有支付 token 的逻辑。支付是在 uniswapV3MintCallback
回调函数中完成的,该函数封装在了被继承的抽象合约 LiquidityManagement 里,以下是该回调函数的实现:
function uniswapV3MintCallback(
uint256 amount0Owed,
uint256 amount1Owed,
bytes calldata data
) external override {
//解码出data数据
MintCallbackData memory decoded = abi.decode(data, (MintCallbackData));
//校验callback的调用者
CallbackValidation.verifyCallback(factory, decoded.poolKey);
//完成支付,其中msg.sender是pool合约
if (amount0Owed > 0) pay(decoded.poolKey.token0, decoded.payer, msg.sender, amount0Owed);
if (amount1Owed > 0) pay(decoded.poolKey.token1, decoded.payer, msg.sender, amount1Owed);
}
核心逻辑很简单,先把 data
解码出来,变成 MintCallbackData
结构体数据,从中可读取出具体参数。
verifyCallback
里面会计算出 pool 地址,并有一行校验:
require(msg.sender == address(pool));
即是说,调用当前 uniswapV3MintCallback
函数的需是 pool 合约。
之后就通过 pay
函数完成支付,从 payer 支付给 msg.sender,即 pool 合约。我们看看 pay
函数的实现:
function pay(
address token,
address payer,
address recipient,
uint256 value
) internal {
if (token == WETH9 && address(this).balance >= value) {
// pay with WETH9
IWETH9(WETH9).deposit{value: value}(); // wrap only what is needed to pay
IWETH9(WETH9).transfer(recipient, value);
} else if (payer == address(this)) {
// pay with tokens already in the contract (for the exact input multihop case)
TransferHelper.safeTransfer(token, recipient, value);
} else {
// pull payment
TransferHelper.safeTransferFrom(token, payer, recipient, value);
}
}
如果第一个 if
语句成立,则说明用户实际支付的是 ETH,这时候就需要把 ETH 转为 WETH 再执行转账,因为底层的池子只接收 ERC20 Token。
为头寸增加流动性
在已有的头寸上是可以再增加流动性的,increaseLiquidity
函数支持该功能。以下是该函数的代码实现:
struct IncreaseLiquidityParams {
uint256 tokenId; //头寸的tokenId
uint256 amount0Desired; //预估添加的amount0
uint256 amount1Desired; //预估添加的amount1
uint256 amount0Min; //滑点保护的最小amount0
uint256 amount1Min; //滑点保护的最小amount1
uint256 deadline; //过期时间
}
function increaseLiquidity(IncreaseLiquidityParams calldata params)
external
payable
override
checkDeadline(params.deadline) //检查是否过期
returns (
uint128 liquidity, //增加的流动性
uint256 amount0, //实际支付的amount0
uint256 amount1 //实际支付的amount1
)
{
//读取出头寸对象实例
Position storage position = _positions[params.tokenId];
//根据poolId读取出poolKey
PoolAddress.PoolKey memory poolKey = _poolIdToPoolKey[position.poolId];
IUniswapV3Pool pool;
//调用内部函数实现流动性的添加
(liquidity, amount0, amount1, pool) = addLiquidity(
AddLiquidityParams({
token0: poolKey.token0,
token1: poolKey.token1,
fee: poolKey.fee,
tickLower: position.tickLower,
tickUpper: position.tickUpper,
amount0Desired: params.amount0Desired,
amount1Desired: params.amount1Desired,
amount0Min: params.amount0Min,
amount1Min: params.amount1Min,
recipient: address(this)
})
);
//计算出头寸的Key
bytes32 positionKey = PositionKey.compute(address(this), position.tickLower, position.tickUpper);
//从头寸数据里获取出最近手续费数据
(, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128, , ) = pool.positions(positionKey);
//将之前赚取的手续费收益结算到tokensOwedX里
position.tokensOwed0 += uint128(
FullMath.mulDiv(
feeGrowthInside0LastX128 - position.feeGrowthInside0LastX128,
position.liquidity,
FixedPoint128.Q128
)
);
position.tokensOwed1 += uint128(
FullMath.mulDiv(
feeGrowthInside1LastX128 - position.feeGrowthInside1LastX128,
position.liquidity,
FixedPoint128.Q128
)
);
//更新最近手续费数据
position.feeGrowthInside0LastX128 = feeGrowthInside0LastX128;
position.feeGrowthInside1LastX128 = feeGrowthInside1LastX128;
//累加流动性
position.liquidity += liquidity;
//发送事件
emit IncreaseLiquidity(params.tokenId, liquidity, amount0, amount1);
}
看其入参,是需要指定头寸的 tokenId
的。实现逻辑的第一行代码就是使用 tokenId
为 key,从 _positions
读取出 Position
对象,后面的逻辑主要目的也是更新该 Position
对象的数据。
然后,也和 mint 函数一样调用了 addLiquidity
函数来完成内部的添加流动性逻辑。之后,会把该头寸内之前的流动性所赚取的手续费收益结算到 Position 对象的 tokensOwed0
和 tokensOwed1
字段,并更新 feeGrowthInside0LastX128
和 feeGrowthInside1LastX128
,以及累加流动性 liquidity
。
最后,同样发送了 IncreaseLiquidity
事件。
减少流动性
可以在一个头寸上增加流动性,自然也可以减少流动性,实现该功能的函数为 decreaseLiquidity
,以下是其代码实现:
struct DecreaseLiquidityParams {
uint256 tokenId; //头寸的tokenId
uint128 liquidity; //待减少的流动性
uint256 amount0Min; //滑点保护的最小amount0
uint256 amount1Min; //滑点保护的最小amount1
uint256 deadline; //过期时间
}
function decreaseLiquidity(DecreaseLiquidityParams calldata params)
external
payable
override
isAuthorizedForToken(params.tokenId) //检查调用者是否有权限操作该头寸
checkDeadline(params.deadline) //检查是否过期
returns (uint256 amount0, uint256 amount1)
{
require(params.liquidity > 0);
//读取出头寸实例
Position storage position = _positions[params.tokenId];
//头寸里的总流动性
uint128 positionLiquidity = position.liquidity;
require(positionLiquidity >= params.liquidity);
PoolAddress.PoolKey memory poolKey = _poolIdToPoolKey[position.poolId];
IUniswapV3Pool pool = IUniswapV3Pool(PoolAddress.computeAddress(factory, poolKey));
//调用底层池子的移除流动性函数
(amount0, amount1) = pool.burn(position.tickLower, position.tickUpper, params.liquidity);
//滑点校验
require(amount0 >= params.amount0Min && amount1 >= params.amount1Min, 'Price slippage check');
bytes32 positionKey = PositionKey.compute(address(this), position.tickLower, position.tickUpper);
// this is now updated to the current transaction
(, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128, , ) = pool.positions(positionKey);
//结算amount0、amount1和手续费收益
position.tokensOwed0 +=
uint128(amount0) +
uint128(
FullMath.mulDiv(
feeGrowthInside0LastX128 - position.feeGrowthInside0LastX128,
positionLiquidity,
FixedPoint128.Q128
)
);
position.tokensOwed1 +=
uint128(amount1) +
uint128(
FullMath.mulDiv(
feeGrowthInside1LastX128 - position.feeGrowthInside1LastX128,
positionLiquidity,
FixedPoint128.Q128
)
);
//更新最新手续费
position.feeGrowthInside0LastX128 = feeGrowthInside0LastX128;
position.feeGrowthInside1LastX128 = feeGrowthInside1LastX128;
//从头寸中减少流动性
position.liquidity = positionLiquidity - params.liquidity;
//发送事件
emit DecreaseLiquidity(params.tokenId, params.liquidity, amount0, amount1);
}
其入参除了指定头寸的 tokenId
,还指定了 liquidity
,这是要移除的流动性数量。
相比前面的添加流动性操作,减少流动性函数还多了另一个函数修饰器 isAuthorizedForToken
,这是为了校验调用者 msg.sender
是否有权限操作该头寸对应的 tokenId
,只有该 tokenId
的 owner
或获得授权的用户才可以执行减少流动性的操作。
实现逻辑倒也简单,先通过 tokenId 从 _positions
读取出 Position 对象,然后校验头寸里的流动性不能小于要移除的流动性。之后,计算出 pool 地址,并调用 pool 合约底层的 burn
函数来实现底层的移除流动性操作。然后,和增加流动性时一样,结算之前的手续费收益并更新手续费相关字段,移除的流动性也相应从头寸中减少。另外,有一点要注意,结算头寸的 tokensOwed
时,除了手续费收益之外,移除流动性计算所得的 amount0
和 amount1
也同样结算到了该字段里。都统一通过 collect 函数进行提取。
最后,发送 DecreaseLiquidity
事件。
提取手续费收益和流动性移除的代币
UniswapV2 的手续费收益是在移除流动性时一起提取走的,但 UniswapV3 的手续费收益是单独提取的,通过 collect
函数进行提取。而且,移除流动性结算所得的两个代币也是通过 collect
函数一起提取。以下为其代码实现:
struct CollectParams {
uint256 tokenId; //头寸的tokenId
address recipient; //接收地址
uint128 amount0Max; //提取的最大amount0
uint128 amount1Max; //提取的最大amount1
}
function collect(CollectParams calldata params)
external
payable
override
isAuthorizedForToken(params.tokenId) //检查调用者是否有权限操作该头寸
returns (uint256 amount0, uint256 amount1)
{
require(params.amount0Max > 0 || params.amount1Max > 0);
//如果参数设置的接收地址为0地址,则实际提取到当前合约地址
address recipient = params.recipient == address(0) ? address(this) : params.recipient;
//读取出头寸对象实例
Position storage position = _positions[params.tokenId];
PoolAddress.PoolKey memory poolKey = _poolIdToPoolKey[position.poolId];
//计算出pool地址
IUniswapV3Pool pool = IUniswapV3Pool(PoolAddress.computeAddress(factory, poolKey));
//缓存两个可提取的金额
(uint128 tokensOwed0, uint128 tokensOwed1) = (position.tokensOwed0, position.tokensOwed1);
// trigger an update of the position fees owed and fee growth snapshots if it has any liquidity
if (position.liquidity > 0) {
//调用底层的burn函数,但传入的流动性为0,可结算最新的手续费收益
pool.burn(position.tickLower, position.tickUpper, 0);
//读取出最新的手续费收益
(, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128, , ) =
pool.positions(PositionKey.compute(address(this), position.tickLower, position.tickUpper));
//结算手续费收益
tokensOwed0 += uint128(
FullMath.mulDiv(
feeGrowthInside0LastX128 - position.feeGrowthInside0LastX128,
position.liquidity,
FixedPoint128.Q128
)
);
tokensOwed1 += uint128(
FullMath.mulDiv(
feeGrowthInside1LastX128 - position.feeGrowthInside1LastX128,
position.liquidity,
FixedPoint128.Q128
)
);
//更新最新的手续费收益
position.feeGrowthInside0LastX128 = feeGrowthInside0LastX128;
position.feeGrowthInside1LastX128 = feeGrowthInside1LastX128;
}
//提取金额最多为入参设置的最大值
(uint128 amount0Collect, uint128 amount1Collect) =
(
params.amount0Max > tokensOwed0 ? tokensOwed0 : params.amount0Max,
params.amount1Max > tokensOwed1 ? tokensOwed1 : params.amount1Max
);
//调用底层的collect函数执行实际的提取操作
(amount0, amount1) = pool.collect(
recipient,
position.tickLower,
position.tickUpper,
amount0Collect,
amount1Collect
);
//从结算字段中减少已提取的数额
(position.tokensOwed0, position.tokensOwed1) = (tokensOwed0 - amount0Collect, tokensOwed1 - amount1Collect);
emit Collect(params.tokenId, recipient, amount0Collect, amount1Collect);
}
入参有 4 个,tokenId
指定要从哪个头寸提取,recipient
是接收地址,amount0Max
和 amount1Max
是要提取的最大数量。提取的代币,是从 Position
对象中的 tokensOwed0
和 tokensOwed1
中提取的。另外 tokensOwed0
和 tokensOwed1
只是记录了上一次结算的收益,因此,还会把从上一次结算到当前的收益也计算出来,也累加到 tokensOwed0
和 tokensOwed1
。并更新最新的 feeGrowthInside0LastX128
和 feeGrowthInside1LastX128
。而且,还会调用 pool 合约底层的 burn
函数来更新手续费,因为没有移除流动性,所以调用 burn
时的第三个参数为 0。如果最后计算出来的 tokensOwed
大于入参的最大提取数量,那就只会提取最大数量。而实际提取的操作,也是在 pool 合约底层的 collect
函数执行的。
销毁头寸
当一个头寸已经没有流动性了,手续费收益等也已经提取完了,则可以将该头寸进行销毁操作。实现逻辑非常简单,以下是其代码实现:
function burn(uint256 tokenId) external payable override isAuthorizedForToken(tokenId) {
Position storage position = _positions[tokenId];
require(position.liquidity == 0 && position.tokensOwed0 == 0 && position.tokensOwed1 == 0, 'Not cleared');
delete _positions[tokenId];
_burn(tokenId);
}
首先,只有该头寸的拥有者或授权者才有权限执行销毁操作。其次,require
里表明了需要该头寸的 liquidity、tokensOwed0、tokensOwed1 都为 0 的情况下才允许销毁。然后,通过 delete _positions[tokenId]
删除该 tokenId
所存储的头寸数据。再通过 _burn(tokenId)
销毁该 NFT。
至此,整个头寸管理合约的核心操作就讲解完毕了。
比推快讯
更多 >>- CME 将于 2026 年初推出加密衍生品 7×24 交易
- 公链活跃度近 7 日排行榜:Solana 稳居首位
- Pine Analytics 发布 Flying Tulip 筹资与机制解析
- 币安 Alpha 平台交易 PINGPONG 和 ASP 分享 200 万美元等值奖励
- Abracadabra 遭攻击,黑客已将盗取 170 万美元全部转入 Tornado Cash
- 观点:比特币巨鲸抛售放缓,新结构性需求显现
- 数据,全网 BTC 合约未平仓量达 898 亿美元
- 比特币市值再次超越亚马逊,全球资产市值排名升至第 7 位
- 数据:过去 24h Binance 净流入 2.58 亿 USDT
- 易理华旗下 Trend Researh 近 15 分钟向 Binance 存入 5083.3 枚 ETH
- SBF:FTX 破产期间最大的错误是将公司控制权给新管理层
- 贝莱德旗下 GIP 拟以 400 亿美元收购 Aligned 数据中心
- VanEck:机构囤积 ETH 使未质押的持有者面临资产稀释风险
- 观点:再次降息预期推动比特币 ETF 需求,Q4 ETF 或买入超 10 万枚比特币
- 某交易员早期买入4代币,实现 652 倍浮盈
- 新加坡 Veloza 科创产业博览会暨 Web3 全球颁奖盛典 13 日开幕,维洛基金会代表、NASA 顾问等全球政商领袖齐聚
- 加密 KOL Unipcs 花费 128 万美元购入4代币,买入均价 0.17 美元
- Bitwise CEO:已提交 Aptos ETF S-1 文件,对 Aptos 生态发展势头感到兴奋
- Aqua Labs 推出 2000 万美元创业支持计划,主要关注 AI 与 Web3 的深度融合等领域
- Buidlpad 推出 Pre-TGE HODL 质押活动,首期项目为 Momentum
- 摩根大通分析师:到 2025 年底,比特币价格或达 165,000 美元
- 数据:过去 24 小时全网爆仓 4.48 亿美元,多单爆仓 1.87 亿美元,空单爆仓 2.61 亿美元
- 萨尔瓦多近 7 日共增持 8 枚 BTC,总持仓 6,338.18 枚
- 英国将于下周解除加密 ETN 零售禁令,ETN 属于与加密资产挂钩的债务证券
- 数据:Debridge 近一个月约 1 亿美元资金通过 Debridge 从 Solana 流向 BNB Chain
- REXShares 与 OspreyFunds 已向 SEC 提交 SEI 质押 ETF 申请
- Bitunix 分析师:MYX24 小时重挫 26.15%,技术崩溃与解锁疑虑扩大卖压
- 越南央行预计信贷增长将推动流动性流入加密货币市场
- 比特币提币延续,过去 24 小时 CEX 净流出 5,739.82 枚 BTC
- 数据:以太坊基金会 DeFi 多签钱包以 4508 美元均价出售 1000 枚 ETH
- 美国国债期权市场预示政府停摆将持续 10 至 29 天
- 数据:当前加密恐慌贪婪指数为 72,处于贪婪状态
- BTC Development Corp. 完成 2.53 亿美元首次公开募股
- RootData:BMT 将于一周后解锁价值约 128 万美元的代币
- Project Hunt:第 1 层可编程数据链 Irys 为过去 7 天新增 Top 人物关注者最多的项目
- FTX:警惕钓鱼邮件与仿冒网站,官方不会要求连接钱包
- Metamask 将推出积分计划,未来可以兑换代币奖励
- 昨日以太坊现货 ETF 净流入 2.31 亿美元
- 数据:Hyperliquid 平台鲸鱼当前持仓 104.7 亿美元,多空持仓比为 0.87
- 某合约巨鲸过去 3 天加仓 2300 万 U 保证金避免清算,其 20 倍 BTC 空单仓位价值 2.49 亿美元
- Citadel Wallet 宣布推出首款原生支持 SUI 的加密硬件钱包 SuiBall,预购已开启
- WLFI:近期以 0.25 美元价格向 Hut8 出售部分 WLFI 作为财库储备
- USDC 发行量突破 750 亿枚,市场份额占据 24.9%
- 数据:Alameda 于 2 小时前将 250 枚 BTC 存入 Binance
- 美法官驳回一起针对 Yuga Labs 的诉讼,裁定 ApeCoin 与 BAYC NFT 不构成证券
- 某新建地址从 Kraken 提币 26,029 枚 ETH,价值 1.168 亿美元
- Plasma 集成 Chainlink,提供预言机、跨链和数据服务
- 数据:ETH 当前全网 8 小时平均资金费率为 0.0052%
- Coinbase正在向美OCC申请联邦信托牌照
- 多家机构同日提交逾20份加密货币ETF申请
比推专栏
更多 >>观点
比推热门文章
- 公链活跃度近 7 日排行榜:Solana 稳居首位
- Pine Analytics 发布 Flying Tulip 筹资与机制解析
- 币安 Alpha 平台交易 PINGPONG 和 ASP 分享 200 万美元等值奖励
- Abracadabra 遭攻击,黑客已将盗取 170 万美元全部转入 Tornado Cash
- 观点:比特币巨鲸抛售放缓,新结构性需求显现
- 数据,全网 BTC 合约未平仓量达 898 亿美元
- 比特币市值再次超越亚马逊,全球资产市值排名升至第 7 位
- 数据:过去 24h Binance 净流入 2.58 亿 USDT
- 易理华旗下 Trend Researh 近 15 分钟向 Binance 存入 5083.3 枚 ETH
- SBF:FTX 破产期间最大的错误是将公司控制权给新管理层