# ViteX 内置合约设计与实现简介

ViteX 是 Vite 链上的内置去中心化交易所,是 Vite 生态的重要组成,ViteX 由 Vite 内置合约 vDex 和链下服务 dexServer 两部分组成。vDex 最初设计的目标是实现所有核心功能全部上链,这些功能包含充值、提现、开通交易对、订单撮合,后来进一步将 VX 代币挖矿,手续费分红,运营商管理等功能上链,在实现资产安全的同时也保证了规则透明、公平。下面对 vDex 的设计与实现的几个关键点做一个介绍。

# 存储设计

相比中心化交易,vDex 的实现会受限于链本身存储结构和性能,为了能最大化提高撮合效率,vDex 同时采用了多种策略优化读写效率。

# 价格有序的订单 id 设计

订单存储的方式决定了写入和读取的效率,也最终决定了撮合的执行效率。Vite 链底层基于 LevelDB 实现存储,支持字节序的顺序遍历,和 vDex Taker-Maker 撮合规则依次匹配价格有序订单的特点一致。vDex 根据 levelDB 字节序的特点设计了下面的订单 id 格式,保证 taker 匹配时只需要一次迭代便利即可完成整个撮合,订单结构实现如下图表示。

订单 id 为定长的 22 字节,各个组成部分具体含义如下:

marketId: 每个交易对的唯一标识,是一个自增的整数值。

side:0 买入 1 卖出,marketId+side 的组成的字节前缀,实现 taker 在一次匹配过程中需要遍历的全部订单有共同的前缀。

price:10 个字节的价格定点数表示,前 5 个字节为整数部分,后 5 个字节为小数部分,能够支持最大 12 位有效整数 + 12 为有效小数的价格。对于卖出,低价订单价格字节序在前;对于买入订单,价格字节取反,实现高价订单字节序在前。

timestamp: 同价订单时间戳在前的字节序在前

serialNo: 价格 + 时间戳相同的订单,通过链上确认挂单的顺序依次排序,保证严格的顺序性。

用以上规则拼接出来的订单编号作为 key 写入 leveDB,实现了字节序遍历即价格序遍历,保证了撮合的效率。

具体撮合过程如下:

撮合引擎通过新 taker 的 marketId+!side 组合一个前缀,通过该前缀字节增序遍历 levelDB 中的 kv,即可获取该市场已经排好序的对手交易深度的全部有序订单列表。taker 和当前遍历到的 maker 是否匹配只需要满足!taker.side && bytes.Cmp (taker.Price, maker.Price) >= 0 || taker.side && bytes.Cmp (taker.Price, maker.Price) <= 0 ,如果满足条件进行具体的订单执行,如果还有未撮合部分就可以依次迭代该列表进行撮合,直到匹配失败或者完权成交为止,如果匹配失败只需要简单的以订单 id 为 key 写入 taker 到存储即可。整个过程对存储的使用方式是最简单也是最高效的。

# 订单对象压缩存储

为了提高存储效率,使用 protocol-buffers 作为订单对象的序列化方式,在保证足够的压缩效率的情况下,也便于后续新增字段来扩展订单结构及向后兼容。

考虑到订单 id 已经包含了 marketId、side、price、timestamp4 个字段,在实际序列化订单对象之前,会将以上字段赋值为空,在反序列化后通过订单 id 解析后再赋值,以进一步节约存储。

# 清理存储

为了减少链上存储,对于匹配完成或者撤销的订单会及时进行空间的清理。同时订单有超时的机制,对于超时订单,可以通过批量的方式进行非验证的撤销。为了保证所有订单可访问,被清理掉的订单会存在链下服务 dexServer 中。

# 挖矿 / 分红指标存储优化

vDex 支持链上交易挖矿、抵押挖矿及手续费分红,在以天为单位的周期结束后对该周期相关指标进行计算并产出挖矿 / 分红结果。为了不影响正常的交易,挖矿操作和周期滚动操作是异步的,这就要保证每个周期结束的状态可回溯,这里是通过快照每个周期结束时的状态来实现的。为了尽量减少快照数,只有新周期相比前一个有效周期出现业务意义上的指标差异时才记录,最大程度减少重复存储。

# 多合约协作

vDex 目前版本由两个内置合约组成,dexFund 承担充值、提现、上币、挖矿、分红等操作,dexTrade 完成实际的挂单、撮合及撤单。

由两个不同合约组合完成交易所功能,从工程角度便于系统拆分和维护,同时方便后续 dexTrade 分片以提供更高的吞吐。

# 其他

# 资产正确性校验

为了保证代码执行的正确性,除了通过单元测试和集成测试保证基本功能正确性外。vDex 还通过实时校验资产一致性的功能,具体方式是将合约内所有资产进行加和并同合约在链上实际的账本余额进行对照,如果一致则可以认为没有因为 i 内部计算逻辑的错误导致资产增发或销毁。校验操作一定程度上等价于对合约的全量代码做了一次集成测试,该操作可以在测试阶段或者正式部署后定时执行,验证合约的正确性。

# vDex 和 dexServer 协作

合约在执行的过程中会修改内部状态,这些内部状态的变更通过 eventLog 同步链下服务 dexServer,满足相关展现及数据统计需求。