合约是基于 Chain33 区块链框架的交易所戳和合约,基于内存完成转账及买卖交易操作
约定
合约内所有数字都是以 1e8
为基数的整数,即现实生活中的 1
在合约内的值为 100000000
,0.001
为 100000
协议
1 | message WriteRequest { |
交易所核心业务:
- transfer:转帐,用户冲提币,合约内完成相应代币的资产转移操作,涉及到用户资产变更
- order:用户挂单,合约内完成订单生成、戳和,涉及到用户资产变更、交易对深度变更、最新成交单变更
- cancel:用户撤单,合约内完成订单撤销逻辑,涉及到用户资产变更、交易对深度变更
- rate:管理员设置手续费率
- froze:管理员冻结账户资产
- upgrade:合约升级
账户模型
- 一个账户 Account 包含多个资产子账户 Balance(现货交易账户、杠杠交易账户、交割合约账户、永续合约账户、托管合约账户)
- 每个资产子账户 Balance 包含多个币种的资产 WalletInfo
- 每个币种的资产 WalletInfo 包括活跃资产、冻结资产
1 | type Account struct { |
存储结构
交易在合约内执行 Exec
逻辑返回的结果都是 event 列表,event 最终持久化到 state db
和 local db
中,各种 event 定义如下:
1 | message Event { |
state db (datadir/mavltree/store.db)
中的存放的是账户及资产信息 EventAccount
、公用的匹配信息 EventMatch
:
1 | .-mvcc-.d.mavl-exchange-acc:4a246cd2a3f41b2bc1d071d2db159a388cd6f5c3547ea592d401f270073133d7.00000000000000000002 |
localdb (datadir/blockchain.db)
中的存放的是活跃订单 orderPlaced 、历史订单 orderCanceled + orderClosed、成交信息 mtf、交易回报信息 receipt、资产转账记录 transfer:
1 | closeorder:0000131087:6bdb231b0aa00f91261e5056c0c01b14327703230115715ab83894cc43c89d66:00000000000000000015 |
初始化
合约在内存中维护了一个全局的单例戳和对象 ts,Check、Exec、Query、ExecLocal 之前都要保证该单粒对象已初始化且只初始化一次。
1 | type TradeServer struct { |
TradeServer
初始化方法 trade.Init
:
- 加载配置文件 trade.toml
- 从 state db 加载并初始化全局戳和对象 MatchCommon
- 从 local db 加载并初始化各交易对戳和对象 Match
交易检查
- 签名验证:合约内用
uid
标示这笔交易的发起者,采用ed25519
算法进行签名和验签。 - 交易查重:合约内部每笔交易都有一个
instructionId
标识别本次操作, 除了框架本身会根据 hash 进行查重,合约内部还会根据instructionId
进行查重(考虑后续版本去掉合约内查重,提升交易性能)。
转账
合约内的代币都是由 admin 账户创建,admin 分发给各个 bank:
- 代币生成: 从 admin 转到 bank
- 用户冲币: 从 bank 转到用户
- 用户提币: 从用户转到 bank
此外,还支持:
- bank 间转账:
- admin 生成的代币,转到一个大 bank,方便财务人员对交易所内的代币源头进行管控
- 大 bank 分发代币给各个业务模块(冲币、提币、矿机、手续费、机器人等)的小 bank
- 普通用户间转账:方便用户站内冲提币
下单
流程如下:
戳和
- 买卖盘是以价格为 score 的跳跃表 SkipList,买盘按照价格从高到低,卖盘按照价格由低到高
- 相同价格的订单按照挂单时间先后顺序构成 List
- MatchCommon 记录的用户活跃订单表中,每个用户的活跃订单是 SkipList
交易对规则
以 BTC/USDT 为例,BTC 为目标币,USDT 为基础货币:
- 挂买:花费 USDT,得到 BTC
- 挂卖:花费 BTC,得到 USDT
戳和规则
按照 价格优先、时间优先 顺序
手续费规则
手续费率默认为千分之一(管理员可设置用户的手续费率),对买卖双方都收取基础货币作为手续费。
最低手续费:只要订单有成交,则该笔订单收取的手续费必须不少于最低手续费,不同基础货币的最低手续费根据配置略有不同。比如 USDT 交易对的最低手续费为 0.01 USDT, BTC 交易对的最低手续费为 0.000001 BTC。
假设某用户的手续费率为 0.001, 挂单价格为 P,挂单量为 A,则花费 C=P*A
,手续费 F=Min(MinFee, 0.001*C)=Min(MinFee, 0.001*P*A)
。
- 买单:挂单时冻结花费和手续费(>=最低手续费),即实际须冻结 B=C+F,用户相关交易对的基础币可用资产必须不少于B
- 卖单:挂单时冻结 A 数量的目标币,用户相关交易对的目标币可用资产必须不少于 A
虽然最终买卖双方都被平台抽取了手续费(>=最低手续费),买单和卖单对手续费的处理方式略有不同:
- 对买方而言,挂单时已经把该笔订单的手续费冻结进去了
- 对卖方而言,挂单只需冻结对应数量的目标币即可,手续费是在戳和成交时才收取