值得信赖的区块链资讯!
剖析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。
至此,整个头寸管理合约的核心操作就讲解完毕了。
比推快讯
更多 >>- 日经 225 指数盘初持续走强,涨幅扩大至 4%
- Samourai Wallet 联创因协助洗钱被判处四年监禁
- 数据:900.13 万枚 TRX 从 FarFuture 转入 Binance,价值约 257.76 万美元
- Base 联创 Jesse 将推出 jesse 代币,发布时间 11 月 21 日 1:00
- 英伟达 Q3 财报超预期推动比特币矿企股价盘后上涨,Cipher Mining 涨超 13%
- Machi 再投 25 万美元 USDC 开 ETH 高杠杆多单
- 数据:ETH 当前全网 8 小时平均资金费率为 0.0055%
- Circle 旗下代币化货币市场基金 USYC 已上线 BNB Chain
- 英伟达盈利喜人,为市场打下强心剂
- 数据:过去 1 小时 Binance 净流出 4,165.63 万 USDT
- Samourai钱包联合创始人William Lonergan Hill因运营混币器被判处四年监禁
- 贝莱德在特拉华州注册iShares质押以太坊ETF名称
- 特朗普家族关联项目 World Liberty Financial 转移资金,否认平台安全漏洞
- 纳指期货周四开涨 1%,受英伟达等 AI 概念股推动
- 数据:588.86 万枚 ETHFI 从 Anchorage Digital Custody 转出,价值约 525.88 万美元
- Block发布三年战略蓝图,股价单日飙升8%
- 英伟达带动美股芯片股上涨,AMD、台积电和美光科技均涨超 3%
- 数据:2.49 万枚 SOL 从 Wintermute 转出,经中转后流入 Fireblocks Custody
- DeFi协议Spark搁置加密应用计划,转而专注于机构级基础设施建设
- 美元指数上涨 0.68%,收于 100.228
- 英伟达盘后涨超 4%,数据中心营收超预期
- 英伟达 2026 财年 Q3 营收 570 亿美元,预期 549.23 亿美元
- 阿联酋主权基金表示将长期持有比特币,称其与黄金共同扮演重要角色
- AXIOS:白宫正采取行动,阻止限制AI芯片出口的法案
- 美国国会议员 William Timmons 考虑将 X 头像更换为Pudgy Penguin
- 马斯克发帖感谢特朗普
- 前世界拳击冠军 Andrew Tate 在 Hyperliquid 遭遇“超级爆仓”:70 万美元余额归零
- 新罕布什尔州拟发 1 亿美元比特币债券,寻找私营部门合作方
- 美元兑日元USD/JPY站上157,为今年1月以来首次
- 美联储会议纪要,GDP 增长预期上调,失业率将下降
- 美联储会议纪要显示短期融资市场收紧,准备金趋近充裕水平
- 美联储传声筒:12月投票结果将非常胶着
- 美联储会议纪要建议停止资产负债表缩减
- 美联储会议纪要显示多数与会者支持中性政策立场
- 美联储会议纪要揭示严重内部分歧,决策层在通胀警告中坚持降息
- 美联储会议纪要:多数官员认为随着时间的推移会进一步放松政策,但有几位官员表示不一定会在12月降息
- 美联储会议纪要:多位与会者强调了股市可能出现无序下跌的可能性
- 美联储会议纪要:多位与会者表示,在他们目前的展望下,今年剩余时间保持利率不变是合适的
- 某鲸鱼所持 17,804 枚 ETH 多单被清算,近两个月浮亏超 1000 万美元
- Coinbase被曝测试预测市场与股票代币化交易功能,或于12月17日公布进展
- 交易员押注美联储维持利率不变,加密市场暴跌
- 美联储12月降息预期大幅降温,官员会议前无关键数据可参考
- 美国总统特朗普:曾考虑过让财长贝森特进入美联储,但他不愿意
- 疑似 Bitmine 相关新钱包从 BitGo 接收 24827 枚 ETH,价值 7252 万美元
- 策略师:美联储会议纪要或不确认 12 月降息
- Luma AI获得由Humain领投的9亿美元C轮融资,AMD Ventures等参投
- 汇丰:美元或即将触底,因美联储2026年降息可能性较低
- 据比推数据,ETH现报2934.76美元,1小时跌幅为5.02%,价格波动较大,请谨慎交易,控制风险。
- 美联储 12 月降息 25 个基点的概率升至 51.1%
- 美联储会议纪要或揭示政策制定者内部深度分歧
比推专栏
更多 >>观点
比推热门文章
- Samourai Wallet 联创因协助洗钱被判处四年监禁
- 数据:900.13 万枚 TRX 从 FarFuture 转入 Binance,价值约 257.76 万美元
- Base 联创 Jesse 将推出 jesse 代币,发布时间 11 月 21 日 1:00
- 【比推每日新闻精选】英伟达盈利数据超预期,比特币回升至 9.15 万美元;美联储会议纪要揭示严重内部分歧;贝莱德在特拉华州注册iShares质押以太坊ETF
- 英伟达 Q3 财报超预期推动比特币矿企股价盘后上涨,Cipher Mining 涨超 13%
- Machi 再投 25 万美元 USDC 开 ETH 高杠杆多单
- 数据:ETH 当前全网 8 小时平均资金费率为 0.0055%
- Circle 旗下代币化货币市场基金 USYC 已上线 BNB Chain
- 英伟达盈利喜人,为市场打下强心剂
- 数据:过去 1 小时 Binance 净流出 4,165.63 万 USDT
比推 APP



