跳到主要内容

V3 Locker

V3 Locker 托管由白名单 INonfungiblePositionManager 合约发行的 ERC721 流动性仓位。锁仓期间无法更改 NFT 价格区间;获授权账户可以收取手续费,任何用户都可以增加流动性。

ETERNAL_LOCKtype(uint256).max。永久锁仓无法提取或减少流动性,但可以转移、收取手续费和增加流动性。

写入函数

lock

function lock(LockParams calldata params)
external
payable
returns (uint256 lockId)

将仓位 NFT 转入托管,收取锁仓前已累积的手续费,应用所选锁仓费用并创建锁仓。

字段类型说明
params.nftPositionManagerINonfungiblePositionManager已列入白名单的 Position Manager。
params.nft_iduint256由调用方持有并已授权的 NFT token ID。
params.dustRecipientaddress锁仓前已累积手续费的接收方。
params.owneraddress初始锁仓 Owner;必须非零。
params.additionalCollectoraddress可选的 Additional Collector。
params.collectAddressaddressOwner 自动收取净收益的接收方;必须非零。
params.unlockDateuint256以秒为单位的未来 Unix 时间戳,或 ETERNAL_LOCK
params.countryCodeuint16COUNTRY_LIST 接受的代码。
params.feeNamestring命名费用选项,通常为 DEFAULTr 非空时忽略。
params.rbytes[]使用命名费用时为空;使用动态费用时为 FeeResolver 载荷。
返回值类型说明
lockIduint256新创建的锁仓 ID。

访问权限 / payable: NFT Owner/调用方;payable;nonReentrant。

Position Manager、Owner、收取地址、日期和国家/地区必须有效。调用方必须授权 NFT。在扣除 LP 费用前,仓位原有手续费会先收取至 dustRecipient,避免将这些手续费作为新增锁仓流动性计费。使用原生代币支付固定费用时,msg.value 必须精确匹配;使用 ERC20 支付固定费用时需要 allowance。存储的 ucf 是所选手续费收取费率。函数递增 NONCE 并触发 onLock

params.r 非空时,FEE_RESOLVER.useFee(params.r, msg.sender) 提供动态费用。签名费用载荷会绑定 nonce、用户、链和 Resolver。

collect

function collect(
uint256 _lockId,
address _recipient,
uint128 _amount0Max,
uint128 _amount1Max
) external returns (
uint256 amount0,
uint256 amount1,
uint256 fee0,
uint256 fee1
)

从已锁仓仓位中收取 token0 和 token1 手续费。

名称类型说明
_lockIduint256要收取手续费的锁仓。
_recipientaddress普通调用的净收益接收方;Auto Collector 调用时的协议费用接收方。
_amount0Maxuint128token0 最大收取数量。
_amount1Maxuint128token1 最大收取数量。
返回值类型说明
amount0uint256交付给用户侧接收方的 token0 净数量。
amount1uint256交付给用户侧接收方的 token1 净数量。
fee0uint256协议 token0 费用。
fee1uint256协议 token1 费用。

访问权限 / payable: 锁仓 Owner、additionalCollectorAUTO_COLLECT_ACCOUNT;nonpayable;nonReentrant。

对于普通调用方,协议费用发送至 FEE_ADDR_COLLECT,净收益发送至 _recipient。对于 AUTO_COLLECT_ACCOUNT,费用发送至 _recipient,净收益发送至锁仓的 collectAddress。如果 ucf == 0,Position Manager 会将所有已收取代币直接发送至 _recipient,Auto Collector 调用时亦如此。应将 AUTO_COLLECT_ACCOUNT 视为受信任账户,并避免使用零地址接收方。

collectBatchPreview

function collectBatchPreview(
uint256[] calldata lockIds,
address[] calldata recipients,
uint128[] calldata amount0Maxs,
uint128[] calldata amount1Maxs
) external

模拟多次 collect 调用,并通过主动 revert 返回精确结果。

名称类型说明
lockIdsuint256[]要预览的锁仓。
recipientsaddress[]每次 collect 的接收方参数。
amount0Maxsuint128[]各锁仓的 token0 最大值。
amount1Maxsuint128[]各锁仓的 token1 最大值。

访问权限 / payable: 每一项均采用与 collect 相同的授权;nonpayable;nonReentrant。

所有数组长度必须相等,否则函数以 LengthMismatch() revert。只能通过 eth_call 调用;预览成功时会按设计以以下错误 revert:

error CollectBatchPreviewResult(CollectPreviewItem[] previews);

每一项都包含 Lock ID、接收方、代币地址、净数量和费用。revert 会原子化回滚 Position Manager、代币和 Locker 的状态。任何其他 revert 都表示真实的验证或执行失败。不要将该函数作为交易广播。

increaseLiquidity

function increaseLiquidity(
uint256 _lockId,
INonfungiblePositionManager.IncreaseLiquidityParams calldata params
) external payable returns (
uint128 liquidity,
uint256 amount0,
uint256 amount1
)

向已锁仓仓位增加 token0 和 token1 流动性。

名称类型说明
_lockIduint256接收流动性的锁仓。
params.tokenIduint256必须等于锁仓的 NFT ID。
params.amount0Desireduint256从调用方转入的 token0 最大数量。
params.amount1Desireduint256从调用方转入的 token1 最大数量。
params.amount0Minuint256token0 最小支出数量。
params.amount1Minuint256token1 最小支出数量。
params.deadlineuint256Position Manager 截止时间。
返回值类型说明
liquidityuint128增加的流动性。
amount0uint256使用的 token0 数量。
amount1uint256使用的 token1 数量。

访问权限 / payable: 任意调用方;payable;nonReentrant。

调用方必须授权两种代币。未使用的代币会退还给调用方,并触发 onIncreaseLiquidity。该实现不会转发 msg.value;集成方应发送零原生代币。在兼容的情况下,直接与 Position Manager 交互可能更节省 Gas。

decreaseLiquidity

function decreaseLiquidity(
uint256 _lockId,
INonfungiblePositionManager.DecreaseLiquidityParams calldata params
) external payable returns (uint256 amount0, uint256 amount1)

定时锁仓到期后移除流动性,并将所得代币发送给 Owner。

名称类型说明
_lockIduint256要减少流动性的锁仓。
params.tokenIduint256必须等于锁仓的 NFT ID。
params.liquidityuint128要移除的流动性。
params.amount0Minuint256计入的 token0 最小数量。
params.amount1Minuint256计入的 token1 最小数量。
params.deadlineuint256Position Manager 截止时间。
返回值类型说明
amount0uint256decreaseLiquidity 计入的 token0 数量。
amount1uint256decreaseLiquidity 计入的 token1 数量。

访问权限 / payable: 仅锁仓 Owner;payable;nonReentrant。

锁仓必须已到期且不是永久锁仓。减少流动性前,当前全部应计手续费会通过 Locker 的费用路径收取;之后 NFT 应付的全部代币都会收取给调用方。该实现不会转发 msg.value;请发送零值。触发 onDecreaseLiquidity

relock

function relock(uint256 _lockId, uint256 _unlockDate) external

延长锁仓期限或将其设为永久锁仓。

名称类型说明
_lockIduint256要延长的锁仓。
_unlockDateuint256以秒为单位的新时间戳,或 ETERNAL_LOCK

访问权限 / payable: 仅锁仓 Owner;nonpayable;nonReentrant。

日期必须同时大于当前 unlockDateblock.timestamp,且除永久锁仓外必须小于 10_000_000_000。锁仓期限永远不能缩短。触发 onRelock

withdraw

function withdraw(uint256 _lockId, address _receiver) external

从已到期的定时锁仓中转出完整 NFT。

名称类型说明
_lockIduint256要提取的锁仓。
_receiveraddressNFT 接收方。

访问权限 / payable: 仅锁仓 Owner;nonpayable;nonReentrant。

锁仓必须已到期且不是永久锁仓。如果 ucf > 0,会先以 _receiver 作为普通接收方收取全部手续费。随后转移 NFT,删除用户索引项和 LOCKS 状态,并触发 onWithdraw。应使用能够接收 safe ERC721 转账的接收方。

setAdditionalCollector

function setAdditionalCollector(
uint256 _lockId,
address _additionalCollector
) external

设置可调用 collect 的可选账户。

名称类型说明
_lockIduint256要配置的锁仓。
_additionalCollectoraddressCollector 地址,或使用零地址清除。

访问权限 / payable: 仅锁仓 Owner;nonpayable;nonReentrant。更新锁仓并触发 onSetAdditionalCollector

setCollectAddress

function setCollectAddress(
uint256 _lockId,
address _collectAddress
) external

设置自动收取净收益的接收方。

名称类型说明
_lockIduint256要配置的锁仓。
_collectAddressaddress非零的自动收取接收方。

访问权限 / payable: 仅锁仓 Owner;nonpayable;nonReentrant。触发 onSetCollectAddress

transferLockOwnership

function transferLockOwnership(
uint256 _lockId,
address _newOwner
) external

发起两步式锁仓所有权转移。

名称类型说明
_lockIduint256要转移的锁仓。
_newOwneraddress拟议 Owner。

访问权限 / payable: 仅锁仓 Owner;nonpayable;nonReentrant。

新 Owner 必须与调用方不同。合约不会拒绝零地址,但零地址 pending Owner 无法接受转移。已有待定所有权会被覆盖。触发 onLockOwnershipTransferStarted

acceptLockOwnership

function acceptLockOwnership(
uint256 _lockId,
address _collectAddress
) external

接受待定所有权转移,并选择新的自动收取地址。

名称类型说明
_lockIduint256要接受的锁仓。
_collectAddressaddress新的存储收取地址。

访问权限 / payable: 仅 pending Owner;nonpayable;nonReentrant。

用户索引会被移动,pendingOwneradditionalCollector 会被清除,并存储 _collectAddress。与 setCollectAddress 不同,该函数不会拒绝零地址;客户端应拒绝。触发 onTransferLockOwnership

查询函数

getLock / LOCKS

function getLock(uint256 _lockId)
external view returns (Lock memory _lock);
function LOCKS(uint256 lockId) external view returns (
uint256 lock_id,
INonfungiblePositionManager nftPositionManager,
address pool,
uint256 nft_id,
address owner,
address pendingOwner,
address additionalCollector,
address collectAddress,
uint256 unlockDate,
uint16 countryCode,
uint256 ucf
);

两者返回相同的锁仓字段;getLock 返回 struct,而 public mapping Getter 返回 ABI tuple。已提取或未知 ID 返回全零锁仓。

参数说明
_lockId / lockId要读取的锁仓 ID。

getLocksLength / NONCE

function getLocksLength() external view returns (uint256);
function NONCE() external view returns (uint256);

两者都返回下一个 Lock ID,其中包括已提取锁仓的历史记录;两者都不是有效锁仓数量。

用户枚举

function getNumUserLocks(address _user)
external view returns (uint256);
function getUserLockAtIndex(address _user, uint256 _index)
external view returns (Lock memory);
参数说明
_user要查询有效锁仓集合的 Owner。
_index从零开始的有效集合索引;越界访问会 revert。

第一个函数返回有效锁仓数量;第二个函数解析索引对应的 ID 并返回其锁仓。

费用查询

function getFee(string memory _name)
public view returns (FeeStruct memory);
function getFeeOptionAtIndex(uint256 _index)
external view returns (FeeStruct memory);
function getFeeOptionLength() external view returns (uint256);
参数说明
_name精确的费用选项名称;未知名称会 revert。
_index从零开始的费用集合索引;无效索引会 revert。

FeeStruct 包含 namelpFeecollectFeeflatFeeflatFeeToken。由于选项可以立即变更,应在锁仓前即时读取。

nftPositionManagerIsAllowed

function nftPositionManagerIsAllowed(address _nftPositionManager)
external view returns (bool);

返回所提供 Position Manager 是否可供 lock 使用。

公共配置 Getter

function ETERNAL_LOCK() external view returns (uint256);
function FEE_DENOMINATOR() external view returns (uint256);
function COUNTRY_LIST() external view returns (address);
function FEE_RESOLVER() external view returns (address);
function AUTO_COLLECT_ACCOUNT() external view returns (address);
function FEE_ADDR_LP() external view returns (address);
function FEE_ADDR_COLLECT() external view returns (address);
function owner() external view returns (address);

这些由编译器生成或继承的 Getter 会公开永久锁仓和费用常量、外部依赖、特权地址以及两步式合约所有权。

事件

onLock

event onLock(uint256 lock_id, address nftPositionManager, uint256 nft_id, address owner, address additionalCollector, address collectAddress, uint256 unlockDate, uint16 countryCode, uint256 collectFee, address poolAddress, INonfungiblePositionManager.Position position);

lock 创建锁仓时触发。事件包含 ID、相关账户、解锁条款、存储的 ucf 费率、Pool 地址和完整 Position 快照。

参数说明
lock_id新锁仓 ID。
nftPositionManager定义 NFT 的 Position Manager。
nft_id仓位 NFT ID。
owner初始锁仓 Owner。
additionalCollector可选 Collector。
collectAddress自动收取净收益接收方。
unlockDate解锁时间戳或 ETERNAL_LOCK
countryCode已验证的国家/地区代码。
collectFee存储的 ucf 费率。
poolAddress该仓位对应的 Factory 池。
position完整的 Position Manager Position 快照。

onWithdraw

event onWithdraw(uint256 lock_id, address owner, address receiver);

在删除已提取锁仓状态前触发。

参数说明
lock_id已提取的锁仓。
owner锁仓 Owner。
receiverNFT 接收方。

onLockOwnershipTransferStarted

event onLockOwnershipTransferStarted(uint256 lockId, address currentOwner, address pendingOwner);

当前 Owner 提议新 Owner 时触发。

参数说明
lockId正在转移的锁仓。
currentOwner当前 Owner。
pendingOwner拟议 Owner。

onTransferLockOwnership

event onTransferLockOwnership(uint256 lockId, address oldOwner, address newOwner, address newCollectAddress);

pending Owner 接受转移时触发。

参数说明
lockId已转移的锁仓。
oldOwner原 Owner。
newOwner接受转移的新 Owner。
newCollectAddress接受转移时提供的收取地址。

onSetAdditionalCollector

event onSetAdditionalCollector(uint256 lockId, address additionalCollector);

Owner 变更 Additional Collector 时触发,该地址可以为零。

参数说明
lockId已更新的锁仓。
additionalCollector新 Collector,可以为零地址。

onSetCollectAddress

event onSetCollectAddress(uint256 lockId, address collectAddress);

Owner 变更非零自动收取地址时触发。

参数说明
lockId已更新的锁仓。
collectAddress新的非零接收方。

onRelock

event onRelock(uint256 lockId, uint256 unlockDate);

延长锁仓后触发。

参数说明
lockId已延长的锁仓。
unlockDate新解锁时间戳。

onIncreaseLiquidity

event onIncreaseLiquidity(uint256 lockId);

通过 Locker Wrapper 增加流动性后触发。

参数说明
lockId已增加流动性的锁仓。

onDecreaseLiquidity

event onDecreaseLiquidity(uint256 lockId);

通过 Locker 减少已到期锁仓的流动性后触发。

参数说明
lockId已减少流动性的锁仓。

错误

Locker 业务错误

Error / Revert相关函数触发条件
OWNER CANNOT = address(0)lockparams.owner 为零地址。
COLLECT_ADDRlocksetCollectAddresscollectAddress 为零地址。注意 acceptLockOwnership 当前不会执行该检查。
MILLISECONDSlockrelock普通解锁时间戳大于等于 10_000_000_000,疑似误传毫秒时间戳。
DATE PASSEDlockrelock解锁时间不晚于当前区块时间。
COUNTRYlockcountryCode 未通过 COUNTRY_LIST 校验。
INVALID NFT POSITION MANAGERlockPosition Manager 未加入白名单。
NOT FOUNDlockgetFee指定的命名费用不存在。
FLAT FEElock原生代币 msg.value 与命名费用要求不完全相等。
Gas token transfer failedlockadminRefundEth原生代币向费用地址或退款接收方转账失败。
OWNERcollectcollectBatchPreview、锁仓管理函数调用方不是允许的 Owner、Additional Collector、Auto Collector 或 pending Owner。
NFT IDincreaseLiquiditydecreaseLiquidityparams.tokenId 与锁仓记录中的 nft_id 不一致。
ETERNAL_LOCKdecreaseLiquiditywithdraw尝试从永久锁仓减少流动性或提取 NFT。
NOT YETdecreaseLiquiditywithdraw普通锁仓尚未到期。
DATErelock新解锁时间没有严格大于当前 unlockDate
SAME OWNERtransferLockOwnership_newOwner 与当前 Owner 相同。
LsetUCF_ucf 没有严格小于锁仓当前费率。
DEFAULTremoveFee尝试删除内置 DEFAULT 费用选项。
Fee not existsremoveFee要删除的费用选项不存在。

批量预览自定义错误

error CollectBatchPreviewResult(CollectPreviewItem[] previews);
error LengthMismatch();
Custom Error含义
CollectBatchPreviewResult(...)collectBatchPreview 的预期成功载荷。客户端应将解码后的 previews 作为结果。
LengthMismatch()lockIdsrecipientsamount0Maxsamount1Maxs 的长度不一致。

外部依赖和继承错误

Error / Revert触发条件
STFTransferHelper.safeTransferFrom 失败,通常由余额或 allowance 不足导致。
STTransferHelper.safeTransfer 失败。
SATransferHelper.safeApprove 失败。
Ownable: caller is not the owner非合约 Owner 调用管理函数。
ReentrancyGuard: reentrant call对 nonReentrant 函数发起重入调用。
Panic(0x11)Solidity 算术发生下溢或上溢。