NAV
Im token logo imToken API Documentation DApp-SDK Tokenlon-Onboarding Tokenlon-MMSK Tokenlon-MMProxy
  • 一、概要
  • 二、合约
  • 三、做市商接入
  • 中文

    一、概要

    Tokenlon 是 imToken 旗下的去中心化交易平台,旨在为 imToken 用户提供「速度快」「价格优」「币种多」的去中心化交易服务。核心交易协议采用传统OTC业务RFQ/RFS模型。用户无需将币转出钱包,即可在 Tokenlon 方便快捷完成币币兑换。如何使用?点击 imToken APP 底部导航「市场」,可见页面上方「Tokenlon」模块,选择你要兑换的交易,即可开始交易。

    1. 报价模式

    Tokenlon 使用的是柜台报价模式。何谓柜台报价模式,简而言之 Tokenlon 就像一个 Token 兑换的汇率柜台,用户可以到 Tokenlon 去询问想要兑换 Token 的汇率,如果接受该汇率,就可以使用该汇率进行兑换交易。

    报价架构

    用户询价过程如上图所示,用户提出请求,Tokenlon Server 会请求不同的做市商拿回最优报价返回给用户。

    2. 做市商报价

    当用户 session 建立后,Tokenlon Server 会每隔 4s 请求一次支持该交易对的做市商,所有做市商的价格返回后会进行一次聚合,将最优报价返回到用户。

    做市商的架构大致如下:

    做市商架构

    以上 MMProxy 和 MMSK 由 imToken 提供,MMProxy 和 MMSK 需要做市商自行部署和管理(MMProxy 会由 imToken 提前部署在以太坊网络上)。

    3. 链下报价,链上成交

    快速的柜台报价模式决定了大部分的价格都会被丢弃,为了提高撮合效率,Tokenlon 使用了链下报价,链上成交的模式。

    链下报价的优势:

    • 高效:现有链上报价模式受限于出块速度(算法报价例外,但是不能完全满足做市策略需求)。

    • 精准:链下报价价格是多少,链上成交即是多少,可以满足所见即所得的需求。链上报价受限于出块速度,抢跑等因素的影响,最终成交价格不确定(或失败)。

    用户在获得源源不断的链下订单后,可以选择最新的一笔进行成交。该订单被用户签名并发送到以太坊网络。最终,以太坊网络的智能合约会处理这笔交易,将用户的资产和做市商的资产进行交换,完成币币兑换。

    交易上链

    二、合约

    1. 整体架构

    Tokenlon 协议是基于 0x 协议 的闪兑协议合约,借助 0x 协议完成币币原子兑换。支持 ERC20 进行的ETH/Token,Token/ETH 和 Token/Token 兑换。

    协议由 TokenlonExchange、MarketMakerProxy、UserProxy 三部分组成。

    合约架构

    整个交易如上图所示,将交易发送到 TE 合约,经过合约的检查后交易会发送到 0x Exchange(0x 协议)。图中可以看到,0x Exchange 后面有一个 0xERC20Proxy 的合约,这个合约是用来执行 token 兑换的合约。0x Exchange 确认交易无误后(通常是双方签名验证和余额检查等),0xERC20Proxy 会将 UserProxy 和 MMProxy 中的 token 按照订单中要求的额度进行兑换。

    2. MMProxy

    每个做市商都有一个独立的 MMProxy 合约作为存储 Token 的资金池。这意味着,交易发生时,用户需要的 token 会从 MMProxy 中扣除,相应的,从用户那里扣除的 token 会存储到 MMProxy 中。因此,做市商应该时常保持 MMProxy 中有足量的 token,如果在交易过程中,某种 token 余额不足会导致交易最终失败,这时不会扣出用户或合约中的任何资金。

    交易中 token 扣除是如何进行的?

    上一节中了解到,token 最终是由 0xERC20Proxy 合约进行交换。那么这个交换具体是如何进行的呢?

    订单的是由 MMSK 根据做市商的报价引擎产生的,这个订单会由一个叫做 SIGNER 的钱包地址进行签名,在密码学上保证该订单的合法性。这个订单会包含 tokenA-tokenB 的基本信息,如 tokenA 的 amount 1 和 tokenB 的 amount 100,那么这时候价格实际上就是 100/1 = 100.0(即 1 个 tokenA 可以兑换 100 个 tokenB)。

    用户确认以 100.0 的价格成交订单。用户会用自己的 USER 钱包对订单进行签名,并将交易发送到 TE 合约。如上一节中所示,这笔交易会流转到 0x Exchange 合约。

    交易验证

    0x Exchange 收到交易后,对订单的签名进行验证。Exchange 会分别调用 UserProxy 和 MMProxy 的 isValidSignatrue 方法,若这两个方法返回 true,那么签名校验成功,进入 0xERC20Proxy 的代币清算阶段。

    接下来,0xERC20Proxy 会按照订单里的信息,tokenA 1 个和 tokenB 100 个,将 1 个 TokenA 从 UserProxy 转移到 MMProxy 中,将 100 个 TokenB 从 MMProxy 中转移到 UserProxy 中。

    如何进行资金 rebalance?

    如果持续有用户将 TokenA 换成 TokenB,那么 MMProxy 中的 TokenA 会越来越多,TokenB 则越来越少,这时候就需要进行资金 reblance。

    MMProxy 合约中提供 withdraw 接口,做市商可以使用相应的 MMProxy 权限调用 withdraw 接口提现 MMProxy 中的 tokenA。对于余额不足的 tokenB,做市商使用任何钱包发送 tokenB 到 MMProxy 合约地址即可。

    如 kovan 环境下存在 KNC token 和提现收款地址:

    使用 DApp 中的 Withdraw 进行提现,DApp 中对应参数如下图所示:

    DApp 参数

    其中,第一项为需要提现的 token 地址,第二项为提现的收款地址,第三项是提现的额度。

    注意:提现额度为 uint 类型,如过要提现 1 WETH,即为 1 * pow(10,18),即 1000000000000000000,pow 中的 18 为 WETH 的 decimals。

    如何保障 MMProxy 的安全性?

    在合约中,我们设置定了三种权限,分别是 owner,operator 和 signer。

    合理的使用三个权限能保证 MMProxy 的安全性。参考

    • MMProxy 合约会由 imToken 统一部署后分发给各做市商。并将 MMProxy 的最高权限(owner)移交到做市商。

    三、做市商接入

    做市商接入分两部分,一是配置合约,将 token 转入合约;二是部署 MMSK,并能提供报价到 MMSK。

    1. 配置 MMProxy 合约

    MMProxy 是做市商的资金池,做市商所有用于做市的 token 需要充值到该合约中。

    Tokenlon-MMProxy 合约文档

    如何配置 MMProxy 合约

    • 以太坊网络分为主网(mainnet)和测试网络(kovan)。两个网络的 ETH 和 token 都不互不相通,kovan 网络的所有 ETH 和 token 都没有任何价值,仅用于测试使用。
    1. 使用 imToken 创建钱包,然后复制以太坊地址并提供给 imToken,钱包教程

    2. 如果是测试环境,需要将 imToken 切换到开发者模式-kovan 环境进行配置,Prod/Staging 环境跳过该步骤。

      1. 进入 「我」-「关于我们」,连续点击 「imToken 图标」10次,开启开发者模式
      2. 进入 「我」-「使用设置」-「节点设置」-「ETHEREUM」,选择 Development 下任一节点 - 点击保存,切换至 kovan 测试环境(如果要从 kovan 切换到 Prod/Staging,也在这里进行操作)。
    3. 使用 imToken DApp 浏览器打开权限配置 DApp https://consenlabs.github.io/contract-admin/mmp.html?addr={imToken 提供的合约地址}DApp 浏览器使用教程

    4. 在 DApp 中设置 operator,operator 具备合约的所有操作权限。

      1. 如果是测试环境,可以将 owner\operator\signer 设置为同一个钱包地址,Prod 环境建议不同的权限使用不同的钱包地址(参考合约安全操作)。注意:只有相应权限的钱包,进入到 DApp 里才能进行合约相应的操作,否则按钮不可用,就算发送了合约操作交易,该交易也会失败。
    5. 回到 imToken 钱包,将钱包切换到刚才设置的 operator 钱包,如果 operator 就是当前钱包,则不需要进行切换。切换成功后重新进入 DApp。

    6. 使用 opeartor 钱包设置 signer,signer 是配置在 MMSK 中的 marketMakerSigner 保持一致。

    7. 第二章第2节已经提到,需要将支持的 token 授权给 0xERC20Proxy,因此需要进行 token 授权操作。以 kovan 环境为例(以下都是 kovan 环境的地址,Prod/Staging 需要使用 mainnet 地址):

      1. WETH:0xd0a1e359811322d97991e03f863a0c30c2cf029c // 注意:合约中不支持充值 ETH,一律使用 WETH
      2. KNC:0x25570c5f2058efc52ff34e47631f41b5f6595d42
      3. Spender:0xf1ec01d6236d3cd881a0bf0130ea25fe4234003e // 注意:该地址即是 kovan 环境下 0xERC20Proxy 地址
      4. 使用 DApp 中的 SetAllowance 进行授权,DApp 中对应参数如下图所示:

        DApp 参数

      5. 其中,第一项填写的是 token 地址,可以批量填写,用,分隔;第二项中的 spender 地址即 0xERC20Proxy 地址。填写完成后点击 SetAllwance 按钮即可。注意:每个 token 只需要进行一次 SetAllowance,重复设置可能导致交易失败。

    8. 充值 token 到 MMProxy。用任意钱包把指定的 ERC20 token 转到 MMProxy 地址即可。注意:非 ERC20 token 转入 MMProxy 中就无法提现找回。

    2. 部署 MMSK

    MMSK 的作用是自动将报价签名成为订单,和将价格信息等反馈到 Tokenlon Server。

    MMSK 安装步骤见:Tokenlon-MMSK

    注意:部署 MMSK 前应先完成合约配置。其他 MMSK 相关配置信息需要与 imToken 官方进行同步。

    对 MMSK 服务的基本要求

    1. MMSK 接口最低响应时间(即做市商报价引擎最大延时): 1s
      1. 依赖 服务器部署地区,建议部署在香港、日本、新加坡、台湾等地
      2. 依赖 做市商 indicativePriceprice 接口实现(即报价引擎处理效率, 考虑网络延时, 建议计算周期小于400 millionsec)
      3. 依赖 做市商 接口提供方式:推荐采用 zerorpc 方式;即使采用 http 方式提供,需要使用内网请求方式,减少外网请求耗时
    2. indicativePriceprice 接口中
      1. 支持 base, quote 位置互换的价格提供,例如 pairs 中接口中存在 ETH/USDT,做市商需支持以下四个交易参数的价格返回 (其中 amount 始终为 base 代币的数量)
        1. base: 'ETH', quote: 'USDT', side: 'BUY', amount: xxx
        2. base: 'ETH', quote: 'USDT', side: 'SELL', amount: xxx
        3. base: 'USDT', quote: 'ETH', side: 'BUY', amount: xxx
        4. base: 'USDT', quote: 'ETH', side: 'SELL', amount: xxx
      2. 支持 minAmount, maxAmount 返回,并具有数量指导性
        1. minAmount, maxAmount 始终代表 base 代币的最小、最大可成交数量
        2. minAmount 始终小于 maxAmount,并且 minAmountmaxAmount 存在一定的数量输入区间
        3. result: false, exchangeable: false 情况,始终返回 minAmount, maxAmount
        4. 当传递数量 amount 小于 做市商 minAmount 或 大于 做市商 maxAmount,做市商需要返回 result: false, exchangeable: false, message: 'xxx',以响应无法报价的情况
    3. price 接口中,做市商需要根据 uniqId 做锁仓,以及针对相同前缀的 uniqIduniqId: pujft40auniqId: pujft40a-1uniqId: pujft40a-2 认为是同一个 uniqId)做锁仓更新

    3. FAQ

    MMSK 相关

    1. MMSK 部署时的钱包地址和私钥配置是 signer 钱包吗?

    和 MMProxy 代理合约 setSigner 配置的钱包地址是同一个,属于热钱包。根据合约安全建议,另外还需要配置一个冷钱包,用于 MMProxy 代理合约设置操作(operator)角色。

    2. MMSK 中 API 接口采用那种协议?

    接口有 http 和 zerorpc 两种选择。推荐zerorpc, 性能快一点。

    3. 两个做市商报了同样的成交报价,请问优先选择谁?

    Price/Time priority - 看谁最先返回。

    4. 若用户提交一个 1000 ETH 的订单,但是所有的做市场都没有 1000 ETH 对应的报价,请问这个情况如何处理?

    我们要求做市商给出最大和最小可以承受的量,如果超出最大,会直接提示无法下单。

    5. 需不需要基于不同的交易币种,部署多个 MMSK?

    不需要,所有交易品种支持,做市商通过一个接口 pairs 提供,只需要部署一个 MMSK。

    6. 最大最小数量在什么情况下返回?

    最大最小数量在任何情况下(无论是否正常返回 price、exchangeable 为 true还是 false),都需要返回 minAmount 和 maxAmount,并且minAmount 必须和maxAmount 有一定的区间范围。这两个数量作为做市商在该交易情况下,可支持的最大最小数量,一方面用于用户在 imToken 操作界面上较实时反馈,指导用户输入满足条件的数量,另一方面,也是对做市商单笔交易量、仓位的一个保护。

    7. price和 indicativePrice 接口中的 base 和 quote 比如 KNC/ETH 买卖方向不同,这个 base 和 quote 会变向吗?

    会变向,因为我们这边没有固定的交易对。而是各种Token 互换所对应的交易关系,担心如果MMSK 内部转换,会对做市商的报价产生误差。

    考虑 SNT 和 ETH 这两个币,假设我们计算的汇率的 fair value 是 1 ETH = 7 SNT,

    Base = ETH, Quote = SNT, Buy price => 7.01

    Base = ETH, Quote = SNT, Sell price => 6.99

    Base = SNT, Quote = ETH, Buy price => 0.1429

    Base = SNT, Quote = ETH, Sell price => 0.1428

    8. imToken 上并没有买卖 ,只有 ETH->SNT 和 SNT->ETH。用户如果想用 ETH 换 SNT。他有两个价格 0.1429 和 6.99(换算成0.1431), 这个价格最后怎么决定?

    比如左边是ETH 右边是SNT,这时候如果用户在左边输入数量,ETH 会作为BASE,SNT会作为Quote,side 为 SELL,意味着用户想要卖ETH 换 SNT,amount 数量是ETH(base)数量。如果用户在右边输入数量,SNT 会作为BASE,ETH 会作为Quote,side 为 BUY,意味着用户想要用ETH 买 SNT,amount 数量是SNT(base)数量。虽然在界面显示上、用户兑换结果上没有什么大的区别,但主要是为了我们用户输入的一致性考虑好比用户在右侧输入了数量,而我们始终基于一个 SNT 作为base,那势必会有中间的价格换算,一方面这样的价格换算可能对于做市商会存在价格误差,另外一方面,用户可能他本意就是想到手1000个 SNT所以输入了1000SNT,但经过这样的换算,可能最终无法获得完整的1000SNT,而可能是 998.12727463


    合约相关

    1. 合约中各个币的地址是如何获得的? 比如我们新上一个币 omg,setAllowance 时我们怎么知道地址?

    可以先与 imToken 官方确认合约地址,由 imToken 提供。

    2. 交易所提币流程? 是向合约地址提币还是向合约中币的地址转账?

    给 MMProxy代理合约地址充值可交易的 Token 代币,但是不要把非ERC20 提过来,提过来就提现不了,不能转账 ETH,只能转账 WETH。

    3. 测试环境中, ETH转为WETH再向合约地址转账, 生产环境中,交易所的ETH怎么转账同样的操作,也需要转化成 WETH,需要先从交易所体现到 imToken 或其他去中心化钱包,然后使用钱包转账到 WETH 合约 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2,智能合约会自动 1:1 返还 WETH 到你的钱包地址(ETH 提现到自己的钱包,再往 WETH 合约发送 ETH,就能收到等价的 WETH,将 WETH 发送到合约即可)。

    4. 合约安全操作要求 owner 是冷钱包, 怎么 setOperator

    imToken 冷钱包为例,是可以通过观察钱包打开 MMProxy 权限设置 DApp :https://consenlabs.github.io/contract-admin/mmp.html,然后通过冷钱包签名。

    5.交易过程中有手续费的扣除吗?

    目前 Tokenlon 交易不需要手续费,但是订单最终上链结算需要花费矿工费,矿工费是最终上链结算的人出,ETH 兑换其他代币的手续费,是有用户出手续费,代币兑换 ETH 的矿工费用是imToken 出,可以保证交易的速度。做市商 signer 钱包每次只是签名授权操作,不需要花费矿工费。

    6. rebalance 操作除了 withdraw 接口操作 MMProxy 合约,有没有页面操作的地方?

    充值:可以直接从交易所或者钱包转账到 MMProxy 代理合约,但注意不要把非 ERC20 提现进去,非 ERC20 无法从合约中提出!不能转账 ETH,只能转账 WETH。

    提现:可以使用 imToken 打开 DApp 图形化操作。imToken 提供的 DApp 实际上是合约真实接口 MMProxy 的接口工具,熟悉以太坊的做市商也可以自行 encode ABI,发送到 MMProxy 合约即可实现提现等相关功能。