从上述时序图上来看,无论是市价单还是限价单,他们的执行逻辑其实都是一样的,都是EOA账户发起交易,智能合约生成交易单。由交易机器人根据当前市场价格进行匹配,匹配成功后,像智能合约发起执行命令。它们直接唯一的区别就是执行的合约不同(限价单:OrderBook,市价单:PositionRout
从上述时序图上来看,无论是 市价单 还是 限价单,他们的执行逻辑其实都是一样的,都是EOA账户发起交易,智能合约生成交易单。由交易机器人根据当前市场价格进行匹配,匹配成功后,像智能合约发起执行命令。它们 直接唯一的区别就是执行的合约不同(限价单:OrderBook,市价单:PositionRouter)
接下来,我们结合着源码一起来进行分析 这个是创建市价单的入口方法createIncreasePosition
function createIncreasePosition(
address[] memory _path, 代币转换路径数组,表示用于加仓的代币转换路径(数量一般为1或者2)
address _indexToken, 目标代币,即要建立仓位的标的代币(比如 ETH、BTC)。
uint256 _amountIn, 输入的代币数量,用于建立仓位的代币数。
uint256 _minOut, 最低输出数量,用于在代币转换时确定最低接收数量
uint256 _sizeDelta, 仓位增加的大小 倍数
bool _isLong, (true 为多头,false 为空头)
uint256 _acceptablePrice, 可接受的最大价格
uint256 _executionFee, 执行费用
bytes32 _referralCode, 推荐码,用于记录推荐关系
address _callbackTarget 回调目标地址,如果交易成功可以触发回调
) external payable nonReentrant returns (bytes32) {
// 交易执行费必须大于等于最低阈值
require(_executionFee >= minExecutionFee, "fee");
// 设置的交易费用 必须等于 执行费
require(msg.value == _executionFee, "val");
// _path 的大小必须等于1或者2
require(_path.length == 1 || _path.length == 2, "len");
// 将交易执行费用保存到WETH
_transferInETH();
// 用于记录推荐关系。这通常是为了奖励推荐人
_setTraderReferralCode(_referralCode);
if (_amountIn > 0) {
// 主要是将代币从客户地址转移到当前的PositionRouter上
IRouter(router).pluginTransfer(_path[0], msg.sender, address(this), _amountIn);
}
// 执行对参数打包并保存
return _createIncreasePosition(
msg.sender,
_path,
_indexToken,
_amountIn,
_minOut,
_sizeDelta,
_isLong,
_acceptablePrice,
_executionFee,
false,
_callbackTarget
);
}
第一步 还是进行必要的校验,比如执行费用,交易金额,以及path的大小 第二步 将执行费用保存到WETH 第三步 用于记录推荐关系. 这通常是为了奖励推荐人 第四步 将代币从客户地址转移到当前的PositionRouter上,但是要注意,这里面有一个_amountIn > 0 的判断 什么情况下会出现_amountIn =0 呢(用户不需要增加额外资金,使用已有的抵押物增加杠杆的情况下) 第五步 执行对参数打包并保存的逻辑 接下来看具体的创建打包逻辑
function _createIncreasePosition(
address _account,
address[] memory _path,
address _indexToken,
uint256 _amountIn,
uint256 _minOut,
uint256 _sizeDelta,
bool _isLong,
uint256 _acceptablePrice,
uint256 _executionFee,
bool _hasCollateralInETH,
address _callbackTarget
) internal returns (bytes32) {
// 对参数进行打包
IncreasePositionRequest memory request = IncreasePositionRequest(
_account,
_path,
_indexToken,
_amountIn,
_minOut,
_sizeDelta,
_isLong,
_acceptablePrice,
_executionFee,
block.number,
block.timestamp,
_hasCollateralInETH,
_callbackTarget
);
// 将结构体保存到increasePositionRequests里面,将key添加到increasePositionRequestKeys里面
(uint256 index, bytes32 requestKey) = _storeIncreasePositionRequest(request);
// 发送事件
emit CreateIncreasePosition(
_account,
_path,
_indexToken,
_amountIn,
_minOut,
_sizeDelta,
_isLong,
_acceptablePrice,
_executionFee,
index,
increasePositionRequestKeys.length - 1,
block.number,
block.timestamp,
tx.gasprice
);
return requestKey;
}
第一步 对参数进行打包,打包成IncreasePositionRequest结构体 第二步 将结构体保存到increasePositionRequests里面,将key添加到increasePositionRequestKeys里面,其中key是通过account 和 index keccak256hash加密后的值。 index是increasePositionsIndex+1后的数值
function _storeIncreasePositionRequest(IncreasePositionRequest memory _request) internal returns (uint256, bytes32) {
address account = _request.account;
uint256 index = increasePositionsIndex[account].add(1);
increasePositionsIndex[account] = index;
bytes32 key = getRequestKey(account, index);
increasePositionRequests[key] = _request;
increasePositionRequestKeys.push(key);
return (index, key);
}
第三步 发送创建市价单事件
接下来看看 交易机器人执行市价单的逻辑 executeIncreasePosition 是具体执行加仓逻辑的函数
function executeIncreasePosition(bytes32 _key, address payable _executionFeeReceiver) public nonReentrant returns (bool) {
// 根据key获取指定的request
IncreasePositionRequest memory request = increasePositionRequests[_key];
// if the request was already executed or cancelled, return true so that the executeIncreasePositions loop will continue executing the next request
if (request.account == address(0)) { return true; }
// 这里调用 _validateExecution 函数,检查当前请求是否满足执行条件
bool shouldExecute = _validateExecution(request.blockNumber, request.blockTime, request.account);
if (!shouldExecute) { return false; }
// 删除请求防止重复调用
delete increasePositionRequests[_key];
if (request.amountIn > 0) {
uint256 amountIn = request.amountIn;
// 如果path>1 需要将代币进行交换
if (request.path.length > 1) {
IERC20(request.path[0]).safeTransfer(vault, request.amountIn);
amountIn = _swap(request.path, request.minOut, address(this));
}
// 算并扣除用户在执行增加仓位操作时所需支付的手续费后
uint256 afterFeeAmount = _collectFees(request.account, request.path, amountIn, request.indexToken, request.isLong, request.sizeDelta);
// 进行代币转移
IERC20(request.path[request.path.length - 1]).safeTransfer(vault, afterFeeAmount);
}
// 增加仓位,此方法的执行在BasePositionManager里面(下面会进行介绍)
_increasePosition(request.account, request.path[request.path.length - 1], request.indexToken, request.sizeDelta, request.isLong, request.acceptablePrice);
// 执行费支付给执行请求的交易机器人
_transferOutETHWithGasLimitFallbackToWeth(request.executionFee, _executionFeeReceiver);
emit ExecuteIncreasePosition(
request.account,
request.path,
request.indexToken,
request.amountIn,
request.minOut,
request.sizeDelta,
request.isLong,
request.acceptablePrice,
request.executionFee,
block.number.sub(request.blockNumber),
block.timestamp.sub(request.blockTime)
);
// 执行回调逻辑
_callRequestCallback(request.callbackTarget, _key, true, true);
return true;
}
第一步 根据key获取指定的request,并判断是否已经被执行,或者是否存在 第二步 这里调用 _validateExecution 函数,检查当前请求是否满足执行条件 第三步 删除请求防止重复调用 第四步 校验是否有新追加的资金,如果有要判断是否进行swap,然后再进行代币转移 第五步 最为核心的增加仓位,这里面可以看PositionUtils.increasePosition, 里面涉及到市场价格检查,获取治理权限,更新全局空头数据,执行仓位增加操作等等
function _increasePosition(address _account, address _collateralToken, address _indexToken, uint256 _sizeDelta, bool _isLong, uint256 _price) internal {
_validateMaxGlobalSize(_indexToken, _isLong, _sizeDelta);
PositionUtils.increasePosition(
vault,
router,
shortsTracker,
_account,
_collateralToken,
_indexToken,
_sizeDelta,
_isLong,
_price
);
// 发送一个事件通知,用于记录仓位增加操作,并且可能用于计算推荐奖励等
_emitIncreasePositionReferral(_account, _sizeDelta);
}
第六步 执行费支付给执行请求的交易机器人 第七步 执行回调逻辑
杠杆交易的风险 综上所述,我们就可以了解了基本的杠杆加减仓的原理,但是,个人还是不建议玩杠杆,它的爆仓风险还是比较高的,下面两种情况就可能导致爆仓 条件一:抵押品总USD价值 + 仓位盈亏USD价值 < 资金USD费用 + 清算USD费用。 条件二:(抵押品总USD价值 + 仓位盈亏USD价值) * 最大杠杆倍数 < 仓位总USD价值。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!