状态转换
本文档描述了以下状态转换操作:
向交易模块账户存款
从交易模块账户取款
即时现货市场创建
即时永续合约市场创建
即时交割合约市场创建
现货限价订单创建
批量创建现货限价订单
现货市价订单创建
取消现货订单
批量取消现货订单
衍生品限价订单创建
批量创建衍生品限价订单
衍生品市价订单创建
取消衍生品订单
批量取消衍生品订单
子账户之间转账
向外部账户转账
清算持仓
增加持仓保证金
现货市场参数更新提案
交易模块启用提案
现货市场创建提案
永续合约市场创建提案
交割合约市场创建提案
衍生品市场参数更新提案
交易奖励启动提案
交易奖励更新提案
Begin-blocker 处理
End-blocker 处理
存入 Exchange 模块账户
存款操作由 MsgDeposit 执行,该消息包含 Sender、SubaccountId 和 Amount 字段。
注意:SubaccountId 为可选字段,如果未提供,则会根据 Sender 地址动态计算。
步骤
检查
msg.Amount中指定的denom是否为银行供应中存在的有效denom。将代币从个人账户转入 Exchange 模块账户,若失败则回滚操作。
从
msg.SubaccountId获取subaccountID的哈希类型,如果为零子账户,则使用SdkAddressToSubaccountID根据msg.Sender动态计算。将
msg.Amount递增至subaccountID的存款余额。触发
EventSubaccountDeposit事件,包含msg.Sender、subaccountID和msg.Amount。
从 Exchange 模块账户提现
提现操作由 MsgWithdraw 执行,该消息包含 Sender、SubaccountId 和 Amount 字段。
注意:msg.ValidateBasic 函数会验证 msg.Sender 对 msg.SubaccountId 的所有权。
步骤
从
msg.SubaccountId获取subaccountID的哈希类型。检查
msg.Amount中指定的denom是否为银行供应中存在的有效denom。从
subaccountID中减少msg.Amount,若失败则回滚操作。将代币从 Exchange 模块账户发送至
msg.Sender。触发
EventSubaccountWithdraw事件,包含subaccountID、msg.Sender和msg.Amount。
即时现货市场启动
即时现货市场启动操作由 MsgInstantSpotMarketLaunch 执行,该消息包含 Sender、Ticker、BaseDenom、QuoteDenom、MinPriceTickSize 和 MinQuantityTickSize 字段。
步骤
根据
msg.BaseDenom和msg.QuoteDenom计算marketID。检查是否已存在相同的市场启动提案,如果已存在,则回滚操作。
使用
msg.Ticker、msg.BaseDenom、msg.QuoteDenom、msg.MinPriceTickSize、msg.MinQuantityTickSize启动现货市场,若失败则回滚操作。从
msg.Sender向 Exchange 模块账户发送即时上市费用 (params.SpotMarketInstantListingFee)。最后将即时上市费用发送至社区支出池。
即时永续市场启动
即时永续市场启动操作由 MsgInstantPerpetualMarketLaunch 执行,该消息包含 Sender、Ticker、QuoteDenom、OracleBase、OracleQuote、OracleScaleFactor、OracleType、MakerFeeRate、TakerFeeRate、InitialMarginRatio、MaintenanceMarginRatio、MinPriceTickSize 和 MinQuantityTickSize 字段。
步骤
根据
msg.Ticker、msg.QuoteDenom、msg.OracleBase、msg.OracleQuote和msg.OracleType计算marketID。检查是否已存在相同的市场启动提案,如果已存在,则回滚操作。
从
msg.Sender向 Exchange 模块账户发送即时上市费用 (params.DerivativeMarketInstantListingFee)。使用
msg对象中的必需参数启动永续市场,若失败则回滚操作。最后将即时上市费用发送至社区支出池。
即时到期期货市场启动
即时到期期货市场启动操作由 MsgInstantExpiryFuturesMarketLaunch 执行,该消息包含 Sender、Ticker、QuoteDenom、OracleBase、OracleQuote、OracleScaleFactor、OracleType、Expiry、MakerFeeRate、TakerFeeRate、InitialMarginRatio、MaintenanceMarginRatio、MinPriceTickSize 和 MinQuantityTickSize 字段。
步骤
根据
msg.Ticker、msg.QuoteDenom、msg.OracleBase、msg.OracleQuote、msg.OracleType和msg.Expiry计算marketID。检查是否已存在相同的市场启动提案,如果已存在,则回滚操作。
从
msg.Sender向 Exchange 模块账户发送即时上市费用 (params.DerivativeMarketInstantListingFee)。使用
msg对象中的必需参数启动到期期货市场,若失败则回滚操作。触发
EventExpiryFuturesMarketUpdate事件,包含市场信息。最后将即时上市费用发送至社区支出池。
现货限价订单创建
现货限价订单创建操作由 MsgCreateSpotLimitOrder 执行,该消息包含 Sender 和 Order 字段。
步骤
检查现货交易是否已启用,若未启用则回滚操作。
检查订单的价格和数量是否符合市场的最小数量和价格刻度要求。
递增子账户的
TradeNonce。如果现货市场 ID 没有引用有效的现货市场,则拒绝该订单。
使用
TradeNonce计算唯一的订单哈希。如果子账户的可用存款不足以支付该订单所需的资金,则拒绝订单。
从可用余额中扣除订单所需的资金。
将订单存储在临时限价订单存储和临时市场指示器存储中。
注意:临时存储中的订单会在结束区块处理时执行,若未执行则存入长期存储。
批量创建现货限价订单
批量创建现货限价订单操作由 MsgBatchCreateSpotLimitOrders 执行,该消息包含 Sender 和 Orders 字段。
步骤
遍历
msg.Orders,并按照MsgCreateSpotLimitOrder中的步骤创建每个现货限价订单。
现货市场订单创建
现货市场订单创建操作由 MsgCreateSpotMarketOrder 执行,该消息包含 Sender 和 Order 字段。
步骤
检查现货交易是否已启用,若未启用则回滚操作。
检查订单的价格和数量是否符合市场的最小数量和价格刻度要求。
递增子账户的
TradeNonce。如果现货市场 ID 没有引用有效的现货市场,则拒绝该订单。
使用
TradeNonce计算唯一的订单哈希。检查可用余额是否足以支持市场订单。
计算市场订单的最差可接受价格。
从存款的可用余额中扣除冻结的资金。
将订单存储在临时现货市场订单存储和临时市场指示器存储中。
取消现货订单
现货订单取消操作由 MsgCancelSpotOrder 执行,该消息包含 Sender、MarketId、SubaccountId 和 OrderHash 字段。
步骤
检查现货交易是否已启用,若未启用则回滚操作。
如果现货市场 ID 没有引用有效、挂起或已拆除的现货市场,则拒绝该操作。
检查通过
marketID、subaccountID和orderHash是否存在现货限价订单。将保证金冻结金额返还至可用余额。
递增可用余额的保证金冻结金额。
从
ordersStore和ordersIndexStore中删除订单状态。触发
EventCancelSpotOrder事件,包含marketID和订单信息。
批量取消现货订单
批量取消现货订单操作由 MsgBatchCancelSpotOrders 执行,该消息包含 Sender 和 Data 字段。
步骤
遍历
msg.Data,并按照MsgCancelSpotOrder中的步骤取消每个现货订单。
衍生品限价订单创建
衍生品限价订单创建操作由 MsgCreateDerivativeLimitOrder 执行,该消息包含 Sender 和 Order 字段。
步骤
检查衍生品交易是否已启用,若未启用则回滚操作。
如果子账户已在该市场下已下单(注意:限价订单和市场订单不能同时存在),则拒绝该订单。
获取衍生品市场和通过
marketID获取标记价格。获取指定
marketID和subaccountID的订单簿元数据(SubaccountOrderbookMetadata)。确保限价订单有效:
市场配置(市场 ID 和刻度大小)。
子账户交易
nonce。子账户最大订单数量。
如果是减少仓位订单:
确保存在有效的仓位,并且方向相反。
如果订单会导致其他减少仓位订单失效,则拒绝该订单。
如果是限价订单:
确保子账户的存款足以支持保证金冻结。
如果订单方向与现有仓位相反且会导致其他减少仓位订单失效,则取消失效的减少仓位订单。
将订单存储在临时限价订单存储和临时市场指示器存储中。
批量创建衍生品限价订单
批量创建衍生品限价订单操作由 MsgBatchCreateDerivativeLimitOrders 执行,该消息包含 Sender 和 Orders 字段。
步骤
遍历
msg.Orders,并按照MsgCreateDerivativeLimitOrder中的步骤创建每个衍生品限价订单。
衍生品市价订单创建
衍生品市场订单创建操作由 MsgCreateDerivativeMarketOrder 执行,该消息包含 Sender 和 Order 字段。
步骤
检查衍生品交易是否已启用,若未启用则回滚操作。
检查即将下单的
SubaccountID是否已有限价衍生品订单或市场订单,若有则拒绝该订单。(注意:永续市场不能同时下两个市场订单或同时存在限价/市场订单)检查订单的价格和数量是否符合市场的最小数量和价格刻度要求。
递增子账户的
TradeNonce。如果衍生品市场 ID 没有引用有效的衍生品市场,则拒绝该订单。
使用
TradeNonce计算唯一的订单哈希。检查市场订单的最差价格是否达到最优相反方向订单簿的价格。
检查订单/仓位的保证金金额。
如果是减少仓位订单
A. 检查该子账户在该市场的仓位是否为非空。
B. 检查该订单是否能够平仓。
C. 如果 position.quantity - AggregateReduceOnlyQuantity - order.quantity < 0,则拒绝该订单。
D. 如果是卖出仓位,将保证金冻结金额设置为零。
如果不是减少仓位订单
A. 检查可用余额是否足以支持市场订单。
B. 如果子账户的可用存款不足以支付该订单所需的资金,则拒绝订单。
C. 从存款的可用余额中扣除冻结的资金。
对于相反方向的仓位,如果 AggregateVanillaQuantity > position.quantity - AggregateReduceOnlyQuantity - order.FillableQuantity,则可能会使某些现有的减少仓位订单失效,或使新订单本身无效,需要进行相应操作。
将订单存储在临时衍生品市场订单存储和临时市场指示器存储中。
取消衍生品订单
衍生品订单取消操作由 MsgCancelDerivativeOrder 执行,该消息包含 Sender、MarketId、SubaccountId 和 OrderHash 字段。
步骤
检查衍生品交易是否已启用,若未启用则回滚操作。
如果衍生品市场 ID 没有引用有效的衍生品市场,则拒绝该操作。
检查通过
marketID、subaccountID和orderHash是否存在有效的衍生品限价订单。将保证金冻结金额返还至可用余额。
如果订单类型不允许取消,则跳过取消该限价订单。
从
ordersStore、ordersIndexStore和subaccountOrderStore中删除订单状态。更新子账户的订单簿元数据。
触发
EventCancelDerivativeOrder事件,包含marketID和订单信息。
批量取消衍生品订单
批量取消衍生品订单操作由 MsgBatchCancelDerivativeOrders 执行,该消息包含 Sender 和 Data 字段。
步骤
遍历
msg.Data,并按照MsgCancelDerivativeOrder中的步骤取消每个衍生品订单。
批量订单更新
批量更新订单操作由 MsgBatchUpdateOrders 执行,该消息包含 Sender 和 Orders 字段。
步骤
根据指定的
subaccountID,取消所有在SpotMarketIdsToCancelAll和DerivativeMarketIdsToCancelAll中列出的市场 ID 中的订单。遍历
msg.SpotOrdersToCancel,并按照MsgCancelSpotOrder中的步骤取消现货限价订单。如果取消失败,继续下一个订单。取消成功的结果在MsgBatchUpdateOrdersResponse中反映为SpotCancelSuccess。遍历
msg.DerivativeOrdersToCancel,并按照MsgCancelDerivativeOrder中的步骤取消衍生品限价订单。如果取消失败,继续下一个订单。取消成功的结果在MsgBatchUpdateOrdersResponse中反映为DerivativeCancelSuccess。遍历
msg.SpotOrdersToCreate,并按照MsgCreateSpotOrder中的步骤创建现货限价订单。如果创建失败,继续下一个订单。创建成功的结果在MsgBatchUpdateOrdersResponse中反映为SpotOrderHashes。遍历
msg.DerivativeOrdersToCreate,并按照MsgCreateDerivativeOrder中的步骤创建衍生品限价订单。如果创建失败,继续下一个订单。创建成功的结果在MsgBatchUpdateOrdersResponse中反映为DerivativeOrderHashes。
子账户间转账
从子账户之间的转账操作由 MsgSubaccountTransfer 执行,该消息包含 Sender、SourceSubaccountId、DestinationSubaccountId 和 Amount 字段。
步骤
从
msg.SourceSubaccountId提取msg.Amount,如果失败则回滚交易。增加
msg.DestinationSubaccountId的存款余额,增加的金额为msg.Amount。触发
EventSubaccountBalanceTransfer事件,包含SrcSubaccountId、DstSubaccountId和msg.Amount。
备注:对于子账户之间的转账,不需要从银行模块实际转移代币,只需要更改相关记录即可。
转账到外部账户
外部账户转账由 MsgExternalTransfer 执行,该消息包含 Sender、SourceSubaccountId、DestinationSubaccountId 和 Amount 字段。
步骤
从
msg.SourceSubaccountId提取msg.Amount,如果失败则回滚交易。增加
msg.DestinationSubaccountId的存款余额,增加的金额为msg.Amount。触发
EventSubaccountBalanceTransfer事件,包含SrcSubaccountId、DstSubaccountId和msg.Amount。
备注:对于子账户转账,不需要从银行模块实际转移代币,只需要更改相关记录即可。
事件应区分子账户转账和外部转账。
子账户转账和外部转账没有区别,仍然需要保持不同的消息吗?
清算仓位
清算仓位由 MsgLiquidatePosition 执行,该消息包含 Sender、SubaccountId、MarketId 和 Order 字段。
步骤
检查衍生品交易是否已启用以清算衍生品市场中的仓位,若未启用则回滚交易。
如果衍生品市场 ID 没有引用有效的衍生品市场,则拒绝该操作。
根据
marketID获取衍生品市场和标记价格(markPrice)。获取指定
marketID和subaccountID的仓位信息。根据仓位信息计算清算价格(liquidationPrice)和破产价格(bankruptcyPrice)。
确定是进行清算还是销毁仓位,如果无法全部清算,则回滚。
取消仓位持有者在给定市场中创建的所有减仓限价单。
应用资金并更新仓位信息。
取消仓位持有者在给定市场中创建的所有市场订单。
检查并增加子账户的交易序列号(nonce),计算订单哈希。
计算清算订单的哈希值。
将清算订单存储到存储中。
通过匹配仓位和清算订单执行清算。
根据清算的支付结果处理:
正向支付:
将一半的支付金额发送给清算人(激励清算机器人)。
将另一半的支付金额发送到保险基金(激励参与保险基金的用户)。
负向支付 - 四个级别的资金回收步骤:
从交易者的可用余额中回收。
通过取消交易者的限价单,从交易者的锁仓余额中回收。
从保险基金中回收。
如果资金不足,暂停市场并将市场添加到存储中,以便在下一个区块中结算,见
BeginBlocker规范。
如果市场是永续市场,则根据清算价格和数量更新 VWAP 数据。
如果清算订单中仍有剩余,取消订单并返回剩余部分。
增加仓位保证金
增加仓位保证金由 MsgIncreasePositionMargin 执行,该消息包含 Sender、SourceSubaccountId、DestinationSubaccountId、MarketId 和 Amount 字段。
步骤
检查衍生品交易是否已启用以增加衍生品市场的仓位保证金,若未启用则回滚交易。
如果衍生品市场 ID 没有引用有效的衍生品市场,则拒绝该操作。
获取
sourceSubaccountID的存款信息。如果
deposit.AvailableBalance小于msg.Amount,则回滚交易。根据
marketID和destinationSubaccountID获取仓位信息,如果仓位不存在,则回滚交易。从
sourceSubaccountID减少msg.Amount的存款金额。增加仓位保证金
msg.Amount并更新存储中的仓位信息。
交易所启用提案
市场类型的启用通过 ExchangeEnableProposal 执行,该提案包含 Title、Description 和 ExchangeType 字段。
步骤
对提案进行
ValidateBasic验证。如果
p.ExchangeType是现货市场(spot market),则启用现货交易。如果
p.ExchangeType是衍生品市场(derivative market),则启用衍生品交易。
现货市场启动提案
现货市场启动由 SpotMarketLaunchProposal 执行,该提案包含 Title、Description、Ticker、BaseDenom、QuoteDenom、MinPriceTickSize 和 MinQuantityTickSize 字段。
步骤
对提案进行
ValidateBasic验证。验证
BaseDenom和QuoteDenom是否有效。验证基于
msg.BaseDenom和msg.QuoteDenom是否已有相同市场存在。根据交易模块参数计算
RelayerFeeShareRate。注意:对于 INJ 货币,清算者分成比例设置为 100%。保存现货市场,包含计算的
ticker、baseDenom、quoteDenom、exchangeParams.DefaultSpotMakerFeeRate、exchangeParams.DefaultSpotTakerFeeRate、relayerFeeShareRate、minPriceTickSize、minQuantityTickSize、marketID和MarketStatus_Active。
永续市场启动提案
永续市场启动由 PerpetualMarketLaunchProposal 执行,该提案包含 Title、Description、Ticker、QuoteDenom、OracleBase、OracleQuote、OracleScaleFactor、OracleType、MakerFeeRate、TakerFeeRate、InitialMarginRatio、MaintenanceMarginRatio、MinPriceTickSize 和 MinQuantityTickSize 字段。
步骤
对提案进行
ValidateBasic验证。验证
quoteDenom是否有效。根据
ticker、quoteDenom、oracleBase、oracleQuote和oracleType计算marketID。验证指定的
marketID是否已存在有效或无效的永续市场。尝试通过
oracleBase、oracleQuote、oracleScaleFactor和oracleType获取衍生品市场价格,以检查价格预言机。验证
marketID是否存在保险基金。根据交易模块参数计算
defaultFundingInterval、nextFundingTimestamp和relayerFeeShareRate。执行
SetDerivativeMarketWithInfo将市场信息、市场信息和资金对象存储到存储中。
到期期货市场启动提案
到期期货市场启动由 ExpiryFuturesMarketLaunchProposal 执行,该提案包含 Title、Description、Ticker、QuoteDenom、OracleBase、OracleQuote、OracleScaleFactor、OracleType、Expiry、MakerFeeRate、TakerFeeRate、InitialMarginRatio、MaintenanceMarginRatio、MinPriceTickSize 和 MinQuantityTickSize 字段。
步骤
对提案进行
ValidateBasic验证。验证
quoteDenom是否有效。根据
p.Ticker、p.QuoteDenom、p.OracleBase、p.OracleQuote、p.OracleType和p.Expiry计算marketID。验证指定的
marketID是否已存在有效或无效的到期期货市场。如果到期时间已经过去(即
ctx.BlockTime()已经超过),则回滚。尝试通过
oracleBase、oracleQuote、oracleScaleFactor和oracleType获取衍生品市场价格,以检查价格预言机。验证
marketID是否存在保险基金。根据交易模块参数计算
RelayerFeeShareRate。注意:对于 INJ 货币,清算者分成比例设置为 100%。执行
SetDerivativeMarketWithInfo将市场信息和市场信息对象存储到存储中。注意:TwapStartTimestamp设置为到期时间减去 30 分钟的秒数。
现货市场参数更新提案
现货市场参数更新由 SpotMarketParamUpdateProposal 执行,该提案包含 Title、Description、MarketId、MakerFeeRate、TakerFeeRate、RelayerFeeShareRate、MinPriceTickSize、MinQuantityTickSize 和 Status 字段。
步骤
对提案进行
ValidateBasic验证。根据
p.MarketId获取现货市场,如果不存在,则回滚。如果参数不为空,则重置
MakerFeeRate、TakerFeeRate、RelayerFeeShareRate、MinPriceTickSize、MinQuantityTickSize和Status,如果为空,则保持不变。验证
MakerFeeRate是否大于TakerFeeRate。
衍生品市场参数更新提案
衍生品市场参数更新由 DerivativeMarketParamUpdateProposal 处理,包含标题、描述、市场ID、初始保证金比例、维持保证金比例、做市商费用率、交易者费用率、分销商费用分成比例、最小价格刻度、最小数量刻度和状态。
步骤:
对提案进行基本验证。
通过
p.MarketId验证衍生品市场是否存在,如果不存在,回滚交易。如果不为空,则重置初始保证金比例、维持保证金比例、做市商费用率、交易者费用率、分销商费用分成比例、最小价格刻度、最小数量刻度和状态的参数;如果为空,保持原值。
验证做市商费用率大于交易者费用率。
验证初始保证金比例大于维持保证金比例。
调度衍生品市场参数更新,并在
Endblocker上进行最终更新 - 注意:这是由于衍生品市场参数更新的订单更新,需确保此过程中不会发生异常。
交易奖励活动启动提案
步骤
对提案进行基本验证(ValidateBasic)。
不允许存在已启动的活动。
活动开始时间戳必须是未来时间。
活动的报价币种必须存在。
所有开始时间戳必须匹配持续时间。
设置活动数据(奖励池、信息、市场资格和市场积分乘数)。
触发 EventTradingRewardCampaignUpdate 事件。
交易奖励活动更新提案
步骤
对提案进行基本验证(ValidateBasic)。
所有在CampaignRewardPoolsUpdates中的StartTimestamp必须匹配现有活动。
CampaignDurationSeconds不能修改,但必须与当前活动匹配。
CampaignRewardPoolsUpdates不能修改当前活动,可以包含nil值来删除奖励池。
来自CampaignRewardPoolsAdditions的活动开始时间戳必须在未来。
所有活动的quote denoms必须存在。
删除当前活动的数据(信息、市场资格和市场点数乘数)。
设置活动数据(信息、市场资格和市场点数乘数)。
设置奖励池更新。
设置奖励池添加。
触发EventTradingRewardCampaignUpdate事件。
费率折扣计划提案
步骤
验证提案的基本信息
如果当前的费率折扣计划存在,则删除它以及市场资格
定义的报价币种必须存在
如果需要重启费率周期(例如桶数、桶持续时间或报价币种发生变化),则删除所有账户费率桶并重启周期
将第一个已付费桶的时间戳设置为当前区块时间
设置新的费率折扣计划,并删除它以及市场资格
设置新的市场资格
Last updated