跳到主要内容

V2 Locker

V2 Locker 托管由已配置 BDEX V2 Factory 创建的 ERC20 LP 代币。ETERNAL_LOCKtype(uint256).max;永久锁仓无法提取或缩短期限,但可以增加锁仓量、拆分和转移。

写入函数

lockLPToken

function lockLPToken(
address _lpToken,
uint256 _amount,
uint256 _unlock_date,
address payable _referral,
bool _fee_in_eth,
address payable _withdrawer,
uint16 _countryCode
) external payable

从调用方转入 LP 并扣除当前费用后创建锁仓。

名称类型说明
_lpTokenaddressBDEX V2 交易对代币。
_amountuint256扣费前转入的 LP 数量。
_unlock_dateuint256以秒为单位的 Unix 时间戳,或 ETERNAL_LOCK
_referraladdress payable符合条件的推荐人,或零地址。
_fee_in_ethbooltrue 表示使用原生代币支付固定费用;false 表示使用次级代币。
_withdraweraddress payable初始锁仓 Owner。
_countryCodeuint16COUNTRY_LIST 接受的代码。

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

_amount 必须非零。除 ETERNAL_LOCK 外,解锁日期必须是未来时间且小于 10_000_000_000;国家/地区和 Factory 交易对必须有效。调用方需要提供 LP allowance;若选择次级代币付费,还需提供次级代币 allowance。未列入白名单的原生代币费用支付方必须发送与计算结果完全一致的费用。合约不会拒绝零地址 _withdrawer,因此客户端应拒绝。amountinitialAmount 存储的是扣除 LP 费用后的数量;随后 NONCE 递增并触发 onNewLock

relock

function relock(uint256 _lockID, uint256 _unlock_date) external

延长锁仓期限,或将其转换为永久锁仓。

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

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

新值必须大于当前 unlockDate,并通过秒级格式检查。合约不会另外要求它大于 block.timestamp;客户端应要求 _unlock_date > max(current unlockDate, current block time)。合约会从全部剩余数量中扣除当前 LP 费用并触发 onRelock。该函数没有最小接收数量参数。

withdraw

function withdraw(uint256 _lockID, uint256 _amount) external

从已到期的定时锁仓中提取部分或全部资产。

名称类型说明
_lockIDuint256来源锁仓。
_amountuint256要提取的数量;type(uint256).max 表示全部剩余 LP。

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

数量必须非零,锁仓不能是永久锁仓,且必须满足 unlockDate < block.timestamp。全部提取会从 Owner 的有效索引中删除该 ID,但保留 amount 为零的历史 LOCKS 记录。触发 onWithdraw

incrementLock

function incrementLock(uint256 _lockID, uint256 _amount) external

向已有锁仓增加 LP。

名称类型说明
_lockIDuint256接收增资的锁仓。
_amountuint256扣费前提供的 LP 数量。

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

该函数特意不检查 Owner:增资的 LP 归已有锁仓 Owner 所有。数量必须非零且已授权。客户端应验证 LOCKS(_lockID).lpToken != address(0)。扣除当前 LP 费用后,净数量计入 amount,但不计入 initialAmount,并触发 onIncrementLock

splitLock

function splitLock(uint256 _lockID, uint256 _amount) external payable

将锁仓的一部分转入具有相同 Owner 和条款的新锁仓。

名称类型说明
_lockIDuint256来源锁仓。
_amountuint256分配给子锁仓的数量。

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

数量必须非零且不得大于来源锁仓余额。即使是白名单账户,msg.value 也必须等于 gFees.ethFee。子锁仓继承 lpTokenlockDateunlockDate、Owner 和国家/地区代码;其 amountinitialAmount 均等于 _amount。触发 onSplitLockonNewLock

transferLockOwnership

function transferLockOwnership(
uint256 _lockID,
address payable _newOwner
) external

立即转移锁仓及其有效索引归属。

名称类型说明
_lockIDuint256要转移的锁仓。
_newOwneraddress payable新锁仓 Owner。

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

新 Owner 必须与调用方不同。这是无需接受调用的单步转移。合约不会拒绝零地址,因此客户端应拒绝。触发 onTransferLockOwnership

查询函数

LOCKS

function LOCKS(uint256 _lockID) external view returns (
address lpToken,
uint256 lockDate,
uint256 amount,
uint256 initialAmount,
uint256 unlockDate,
uint256 lockID,
address owner,
uint16 countryCode
)

锁仓的 public mapping Getter。amount 是剩余 LP 数量,initialAmount 是创建时的净数量。未知 ID 返回零值。

TOKEN_LOCKS

function TOKEN_LOCKS(address lpToken, uint256 index)
external view returns (uint256 lockID)

返回 LP 代币在 index 位置的历史锁仓 ID。

代币枚举

function getNumLocksForToken(address _lpToken)
external view returns (uint256);
function getNumLockedTokens() external view returns (uint256);
function getLockedTokenAtIndex(uint256 _index)
external view returns (address);
参数说明
_lpToken要返回其历史锁仓数量的 LP 代币。
_index从零开始的全局 LP 代币索引;越界访问会 revert。

全局代币集合不会裁剪,而 TOKEN_LOCKS 包含历史锁仓 ID。

用户枚举

function getUserNumLockedTokens(address _user)
external view returns (uint256);
function getUserLockedTokenAtIndex(address _user, uint256 _index)
external view returns (address);
function getUserNumLocksForToken(address _user, address _lpToken)
external view returns (uint256);
function getUserLockForTokenAtIndex(
address _user,
address _lpToken,
uint256 _index
) external view returns (TokenLock memory);
参数说明
_user要查询有效索引的 Owner。
_lpToken该 Owner 索引中的 LP 代币。
_index从零开始的索引;越界访问会 revert。

这些函数分别返回有效代币组数量、按索引查询的代币、某代币的有效锁仓数量以及完整锁仓。已全部提取的锁仓会被移除。

费用白名单查询

function getWhitelistedUsersLength() external view returns (uint256);
function getWhitelistedUserAtIndex(uint256 _index)
external view returns (address);
function getUserWhitelistStatus(address _user)
external view returns (bool);

这些函数分别返回白名单长度、按索引查询的地址以及成员状态。无效索引会 revert。

公共配置 Getter

function NONCE() external view returns (uint256);
function ETERNAL_LOCK() external view returns (uint256);
function uniswapFactory() external view returns (address);
function COUNTRY_LIST() external view returns (address);
function owner() external view returns (address);
function gFees() external view returns (
uint256 ethFee,
address secondaryFeeToken,
uint256 secondaryTokenFee,
uint256 secondaryTokenDiscount,
uint256 liquidityFee,
uint256 referralPercent,
address referralToken,
uint256 referralHold,
uint256 referralDiscount
);

NONCE 是下一个锁仓 ID,并非有效锁仓数量。百分比字段使用分母 100010 = 1%)。费用可以立即变更;提交写入交易前应重新读取并模拟。

事件

onNewLock

event onNewLock(uint256 lockID, address lpToken, address owner, uint256 amount, uint256 lockDate, uint256 unlockDate, uint16 countryCode);

lockLPToken 以及 splitLock 创建子锁仓时触发。

参数说明
lockID新锁仓 ID。
lpToken已锁仓交易对代币。
owner锁仓 Owner。
amount存储的净数量。
lockDate创建时间戳。
unlockDate解锁时间戳或 ETERNAL_LOCK
countryCode已验证的国家/地区代码。

onRelock

event onRelock(uint256 lockID, address lpToken, address owner, uint256 amountRemainingInLock, uint256 liquidityFee, uint256 unlockDate);

延长锁仓并扣费后触发。

参数说明
lockID已延长的锁仓。
lpToken交易对代币。
owner锁仓 Owner。
amountRemainingInLock净剩余数量。
liquidityFee已扣除的 LP 费用。
unlockDate新解锁时间。

onWithdraw

event onWithdraw(uint256 lockID, address lpToken, address owner, uint256 amountRemainingInLock, uint256 amountRemoved);

部分或全部提取后触发。

参数说明
lockID已提取的锁仓。
lpToken交易对代币。
ownerOwner 和接收方。
amountRemainingInLock剩余 LP。
amountRemoved已转出的 LP。

onIncrementLock

event onIncrementLock(uint256 lockID, address lpToken, address owner, address payer, uint256 amountRemainingInLock, uint256 amountAdded, uint256 liquidityFee);

向锁仓增资 LP 时触发。

参数说明
lockID已增加数量的锁仓。
lpToken交易对代币。
owner受益 Owner。
payerLP 提供方。
amountRemainingInLock新的剩余总量。
amountAdded增加的净 LP 数量。
liquidityFee已扣除的 LP 费用。

onSplitLock

event onSplitLock(uint256 lockID, address lpToken, address owner, uint256 amountRemainingInLock, uint256 amountRemoved);

创建子锁仓时,针对来源锁仓触发。

参数说明
lockID来源锁仓。
lpToken交易对代币。
owner两个锁仓的 Owner。
amountRemainingInLock拆分后来源锁仓的数量。
amountRemoved子锁仓数量。

onTransferLockOwnership

event onTransferLockOwnership(uint256 lockID, address lpToken, address oldOwner, address newOwner);

单步所有权转移完成时触发。

参数说明
lockID已转移的锁仓。
lpToken交易对代币。
oldOwner原 Owner。
newOwner新 Owner。

错误

以下是 V2 Locker 业务路径中可能出现的主要 revert。错误字符串必须按原文匹配,尤其注意合约中的 ZERO WITHDRAWL 使用了该拼写。

Error / Revert相关函数触发条件
TIMESTAMP INVALIDlockLPTokenrelock普通解锁时间戳大于等于 10_000_000_000,疑似误传毫秒时间戳。
DATE PASSEDlockLPToken普通解锁时间不晚于当前区块时间。
INSUFFICIENTlockLPToken_amount == 0
COUNTRYlockLPToken_countryCode 未通过 COUNTRY_LIST 校验。
NOT UNIV2lockLPToken_lpToken 不是已配置 BDEX V2 Factory 创建的交易对。
INADEQUATE BALANCElockLPToken推荐人没有持有满足 referralHold 要求的推荐代币。
FEE NOT METlockLPTokensplitLockmsg.value 与当前所需原生代币费用不完全相等。
NOT OWNERrelockwithdrawsplitLocktransferLockOwnership调用方不是锁仓 Owner。
UNLOCK BEFORErelock新解锁时间没有严格大于当前 unlockDate
ZERO WITHDRAWLwithdraw_amount == 0,或解析后的实际提取数量为零。
ETERNAL_LOCKwithdraw尝试提取永久锁仓。
NOT YETwithdraw普通锁仓尚未到期。
ZERO AMOUNTincrementLocksplitLock增加或拆分数量为零。
OWNERtransferLockOwnership_newOwner 与当前调用方相同。
TransferHelper: APPROVE_FAILEDToken 授权路径目标 ERC20 的 approve 调用失败或返回 false
TransferHelper: TRANSFER_FAILEDToken 转出路径目标 ERC20 的 transfer 调用失败或返回 false
TransferHelper: TRANSFER_FROM_FAILEDlockLPTokenincrementLock余额、allowance 不足,或 ERC20 transferFrom 调用失败。
Panic(0x11)withdrawsplitLock 等数量运算请求数量超过锁仓剩余数量,或费率导致 Solidity 算术下溢/上溢。

管理函数和重入保护还可能返回 OpenZeppelin 的继承错误:

Error / Revert触发条件
Ownable: caller is not the owner非合约 Owner 调用管理函数。
ReentrancyGuard: reentrant call对 nonReentrant 函数发起重入调用。

底层 Factory、LP Token、推荐代币或费用代币产生的 revert 会原样向上传播,前端应保留原始 revert data。