MVM 是一个跨链的智能合约平台,也是连接 Mixin 生态和其他所有主链的桥梁。Quill 是一个支持加密货币的付费阅读的写作平台,是目前应用 MVM 最典型的例子。
本文将结合 Quill 的具体实践,聊聊如何让一个新项目通过接入 MVM,承接 Web3 的需求。
在 Web3 方面 Quill 的主要需求包括
- 用户可以用任意主流钱包登录
- 用户可以用任意主流加密货币进行小额支付购买文章
- 用户可以高频、小额地接收加密货币的收益转账
下面详细讲如何利用 MVM 来方便快捷地实现以上需求。
注册 Mixin 机器人
如果你的项目原本并非 Mixin 应用,需要先在 Mixin 开发者平台注册一个 Mixin 机器人,这可以用作很多 Mixin API 的调用主体,也可以作为项目的收款钱包。
以太坊钱包登录
利用以太坊钱包登录的流程跟一般 Web3 项目并无二致,最简单的方法就是后台生成一个随机数 nonce
,引导用户在前端用钱包(例如 MetaMask )签名后,再将 address
和签名一同发到后端进行签名验证,验证通过即登录成功。
注册 MVM 跨链桥代理帐号
当用户登录成功后,即可帮用户在 MVM 的跨链桥上进行注册,获得一个代理 Mixin 帐号。为了方便讲解,这里假设用户的以太坊地址是 0x7bD230817b9314239A07049AD3240bEA0Ee35396
。
注意实际请求的时候换成用户的以太坊地址。
curl -H 'Content-Type: application/json' https://bridge.mvm.dev/users \
-d '{"public_key":"0x7bD230817b9314239A07049AD3240bEA0Ee35396"}'
得到的返回结果是
{
"user": {
"contract": "",
"created_at": "2022-08-09T11:36:17.025157427Z",
"full_name": "0x7bD230817b9314239A07049AD3240bEA0Ee35396",
"key": {
"client_id": "53277182-0058-3268-85c3-aa67d91bbcb3",
"private_key": "c1IqlzIyTR5oF1mcviU9QxeFQZM-Luyw2WA9vY7_Lk80nT5Vju4G4sKo7-_q8_TOrPN4zXflp5-0_4x0bvLPQQ",
"session_id": "c309ce8a-69ba-404c-8fe9-7727b7afdc6d"
},
"session_id": "c309ce8a-69ba-404c-8fe9-7727b7afdc6d",
"user_id": "53277182-0058-3268-85c3-aa67d91bbcb3"
}
}
请求结果里面的 user_id
(即 53277182-0058-3268-85c3-aa67d91bbcb3
)为该用户唯一对应的代理 Mixin 帐号,用户任意资产的转入都可以通过这个代理帐号来完成。
而 key
里面的 client_id
、 private_key
和 session_id
则是代理帐号的部分私钥信息,可以用来调用 Mixin API 完成必要的查询操作。
不需要担心代理帐号因为暴露了私钥信息,导致资产不安全。Mixin 帐号的资产转账需要
keystore
+ 六位数字 PIN 码共同完成签名验证,上面的私钥信息只足够完成 Mixin API 的查询需要,无法完成资产转移操作。
用户充值
拥有了代理 Mixin 帐号,意味着你的用户虽然来自其他任意钱包(譬如 MetaMask 或者 ImToken),但此刻拥有了一个 Mixin 钱包。
在支付之前,需要先引导用户充值,Mixin 支持超过 40 条主链,基本上所有资产都能支持。充值的过程就是给用户的代理 Mixin 帐号充值,是一个典型的 Mixin 充值过程。
以充值 ETH 为例,ETH 在 Mixin 里的资产 ID 是 43d61dcd-e413-450d-80b8-101d5e903357
,利用 Mixin API 就可以查到用户 ETH 充值地址。
curl -i -X GET -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" https://api.mixin.one/assets/43d61dcd-e413-450d-80b8-101d5e903357
TOKEN
需要按照 Mixin 文档利用用户的私钥信息生成,目前 Mixin 社区已经提供了各种主流语言的 SDK,开发者可以直接使用。
上述请求的返回结果里,有个 deposit_entries
,就是该资产的充值地址。
{
"type": "asset",
"asset_id": "43d61dcd-e413-450d-80b8-101d5e903357",
"chain_id": "43d61dcd-e413-450d-80b8-101d5e903357",
"fee_asset_id": "43d61dcd-e413-450d-80b8-101d5e903357",
"symbol": "ETH",
"name": "Ether",
"icon_url": "https://mixin-images.zeromesh.net/zVDjOxNTQvVsA8h2B4ZVxuHoCF3DJszufYKWpd9duXUSbSapoZadC7_13cnWBqg0EmwmRcKGbJaUpA8wFfpgZA=s128",
"balance": "0",
"deposit_entries": [
{
"destination": "0x62F6BAd43aEd1f285f0a9C71Fb20B2aaf0eb7c0F",
"tag": "",
"properties": ""
}
],
"destination": "0x62F6BAd43aEd1f285f0a9C71Fb20B2aaf0eb7c0F",
"tag": "",
"price_btc": "0.07045842",
"price_usd": "1174.68",
"change_btc": "-0.03513549488278948",
"change_usd": "-0.07620440711555702",
"asset_key": "0x0000000000000000000000000000000000000000",
"mixin_id": "8dd50817c082cdcdd6f167514928767a4b52426997bd6d4930eca101c5ff8a27",
"reserve": "0",
"confirmations": 36,
"capitalization": 0,
"liquidity": "0"
}
例如这个例子里,在以太坊主网向 0x62F6BAd43aEd1f285f0a9C71Fb20B2aaf0eb7c0F
地址转账 ETH,即可以充值到该用户的 MVM 账户上。充值进度可以则用这个 API 来查询。资产到帐之后,用 MVM 的浏览器可以查到用户的 MVM 资产余额。
在业务上,也可以选择直接把用户引导至 MVM 官方的跨链桥完成充值。
用户支付
当用户账户里有资产,就可以发起支付了。
注意,这里说的支付,是指 MVM 用户向任意 Mixin 钱包发起的支付。具体到 Quill 的场景,就是MVM 用户购买文章时,向 Quill 的 Mixin 机器人钱包发起支付。如果是 MVM 用户相互转账,或者向 MVM 其他合约转账,情况则与以太坊一样,正常调用合约转账即可。
支付前的准备
在正式调用支付前,必须确认用户钱包已经切换至 MVM 网络,MVM 网络参数可以参考这里。
Network name:Mixin Virtual Machine
RPC link:https://geth.mvm.dev
ChainID:73927
Symbol:ETH
当新用户在跨链桥注册之后,不仅会得到一个 Mixin 代理帐号,在 MVM 网络中,还会自动生成一个对应的合约地址,该合约地址与 Mixin 代理帐号相对应,这里称之为代理合约 。代理合约的地址可以用这个 API 获取
GET https://api.mvm.dev/user_contract?user=$USER_ID
代理合约地址也可以通过调用 Registry 合约的方式获取,详情可以参考这里的代码。
用户支付分为三种情况,下面分开一一讲解。
以下讲解重在理清支付的流程,完整代码片段可以参考 Quill 的源码,实际使用时也可以直接使用 Mixin 的 SDK 。
支付原生代币
MVM 原生代币是 ETH,所有合约调用支付的 Gas 费也是 ETH。
当用户支付 ETH 时,步骤如下
- 获取用户的代理合约地址;
- 生成收款人信息的
extra
; - 调用跨链桥合约的
release
方法。
本例中,从 API
https://api.mvm.dev/user_contract?user=53277182-0058-3268-85c3-aa67d91bbcb3
可以得到用户的代理合约地址为 0x346f9eD85A090e3afEFC5c1959B0c9E53Eac7cdC
。
然后需要将收款人信息和你想要携带的 memo
信息按照一定格式编译成一个 extra
字符串,具体代码可以参考
fetchExtra(receivers: string[], threshold: number, memo: string): string {
const action = {
receivers,
threshold,
extra: memo,
};
const extra =
RegistryID.replaceAll('-', '') +
StorageAddress.slice(2).toLowerCase() +
this.web3.utils.sha3(JSON.stringify(action)).slice(2) +
JSON.stringify(action)
.split('')
.map((v) => {
return v.charCodeAt(0).toString(16);
}).join('');
return extra;
}
解释一下里面的关键参数。
receivers
+ threshold
共同指定了收款人地址。
譬如如果是转给 Quill,Quill 的机器人钱包地址是 6d8fd25a-1eb1-4afb-b2a6-d34991959b76
,那么
receivers = [ "6d8fd25a-1eb1-4afb-b2a6-d34991959b76" ];
threshold = 1;
如果是向 4swap 转账,因为 4swap 是一个 MTG,所以实际上是给一个 3/5 的多签地址转账,根据 4swap 的文档可知
receivers = [
"a753e0eb-3010-4c4a-a7b2-a7bda4063f62",
"099627f8-4031-42e3-a846-006ee598c56e",
"aefbfd62-727d-4424-89db-ae41f75d2e04",
"d68ca71f-0e2c-458a-bb9c-1d6c2eed2497",
"e4bc0740-f8fe-418c-ae1b-32d9926f5863"
];
threhold = 3;
memo
是随转账携带的自定义文本信息,通常用来标明转账的用途,以便收款人收款后进行相应的操作。例如 Quill 的 memo
格式比较简单,下面是购买某篇文章的 memo
例子。
memo = Base64.urlsafe_encode64({ t: 'BUY', a: uuid }.to_json)
RegistryID
和 StorageAddress
都是常量,可以从跨链桥的 API 获取,一般不会有变动,本地存好就行。
准备好代理合约地址和 extra
之后,就可以正式调用合约了。
下面是合约调用的代码片段。
const BridgeContract = new this.web3.eth.Contract(BridgeABI, BridgeAddress);
BridgeContract.methods
.release(contract, `0x${extra}`)
.send({ from: this.account, value: payAmount.toString() })
.on('transactionHash', success)
.on('error', fail);
其中 BridageABI
和 BridgeAddress
都可以从跨链桥的 API 获取。
contract
是用户的代理合约地址,在本例中即 0x346f9eD85A090e3afEFC5c1959B0c9E53Eac7cdC
。
this.account
是用户的实际以太坊钱包地址,在本例中即 0x7bD230817b9314239A07049AD3240bEA0Ee35396
。
payAmount
是转账的数量,注意原生代币的精度是 18,如果转账 0.1 ETH,实际输入应该是 0.1 × 1e18
。
一切顺利的话,就会唤起用户的钱包(譬如 MetaMask),进行签名,完成支付。
MVM 的 Gas 费用很低,可以设置
gasPrice = 10000000
,实际调用时,每笔转账的 Gas 费目前一般是 0.00000403 ETH 左右。
支付其他代币
除了 MVM 原生代币 ETH,其余资产在 MVM 网络中都是 ERC20 合约代币,支付此类资产的流程如下
- 获取用户的代理合约地址;
- 生成收款人信息的
extra
; - 获取转账代币的合约地址;
- 调用代币合约的
transferWithExtra
方法。
前两步跟前文一样,不再赘述。
Mixin 中每种资产在 MVM 网络中都有对应的合约地址,利用 Mixin 资产的 asset_id
调用 Registry 合约可以查询得到,代码片段可以参考这里。
譬如转账 BTC,调用合约查询得到 BTC 在 MVM 中的合约地址是 0x0e42Ae5649B3a67842AF0F3fC21d09d9b850A694
。
然后就可以调用合约发起支付了,代码片段如下
const IERC20 = new this.web3.eth.Contract(ERC20ABI, assetContractAddress);
IERC20.methods
.transferWithExtra(contract, payAmount.toString(), `0x${extra}`)
.send({ from: this.account })
.on('transactionHash', success)
.on('error', fail);
这里的 ERC20ABI
是资产的合约 ABI,可以从合约地址获取。
assetContractAddress
就是资产的合约地址,BTC 就是0x0e42Ae5649B3a67842AF0F3fC21d09d9b850A694
。
payAmount
是转账的数量,注意这里的精度是 8,跟 Mixin 资产精度一致,如果转账 0.1 BTC,实际输入应该是 0.1 × 1e8
。
一切顺利的话,就会唤起用户的钱包(譬如 MetaMask),进行签名,完成支付。
转账 NFT
将 NFT 从 MVM 用户转到其他 Mixin 帐号,流程跟上述代币转账差不多,只是具体调用合约不一样,这里不再详述,可以参考 Quill 源码中的代码片段。
更简单的支付方案
在 Quill 的实践中发现,新用户注册完成之后,在实际购买行为之前,先要完成网络切换和资产充值这两步,对新用户的门槛比较高。
MixPay 提供了多渠道、多币种支付,然后结算至 Mixin 钱包的支付方案,非常适合 Quill 的场景。所以 Quill 在 MVM 新用户支付时,是直接引导其至 MixPay 的 checkout 页完成支付。MixPay 的具体使用可以参考 MixPay 的文档。
使用 MixPay 时,值得注意的是,项目最后收到的转账并非直接来自用户的帐号,而是来自 MixPay 的帐号,因而需要用其他方式判定实际的付款人。可以采用在 memo
中携带实际付款人信息的方式。
用户收款
根据 Quill 的规则,每次文章被购买,都需要向文章作者和文章的早期读者支付收益。譬如文章被购买 1000 次,将会发起超过 50 万笔转账。
幸运的是,从 Mixin 钱包向 MVM 用户转账时,不需要支付 Gas 费调用合约,而是直接向其代理 Mixin 帐号发起 Mixin 转账即可。
例如本例中,用户的代理 Mixin 帐号是 53277182-0058-3268-85c3-aa67d91bbcb3
,利用 Mixin 机器人调用 Mixin API 直接向其发起转账即可,快速免费。
用户提现
目前有两个开源项目支持 MVM 用户提现的,分别是 Mixin 官方的 MVM Bridge 和来自社区开发者 zed-wong 的 MVG。
如有需要可以参考这两个项目的代码自行实现。或者在业务上,如果用户需要将 MVM 内的资产提现至其他钱包,可以引导用户到任意一个跨链桥上完成。
关于提现,还需要重点提醒一下用户,不能通过 MetaMask 直接跨链提现,必须通过跨链桥调用相关的提现合约才能成功。否则可能丢失资产。
其他补充说明
MVM 实际上由两个部分组成
- 由 7 个节点组成的 MTG,即多签组,负责管理所有 MVM 用户的资产;
- 由 7 个节点组成的 EVM 网络,负责执行 MVM 网络的智能合约。
MVM 相关代码全部开源,7 个节点来自 Mixin 社区长期耕耘的项目方,安全可靠。另外 MTG 节点数量也并非固定不变,后续是可以根据需要增加的。
上述的代理 Mixin 帐号,部分私钥信息是公开的,但是最重要的六位数字 PIN 码是由 MTG 的节点通过 TIP 协议共同管理的,所以资产是安全的。
在 MVM 用户充值和收款时,当代理 Mixin 帐号被检测到有新入账资产,资产并不会在该帐号中留存,MTG 会即时将该资产转入到 MTG 的多签钱包托管,同时在 MVM 网络中铸造相应的 ERC20 资产,转入对应的 MVM 用户帐号。
在 MVM 用户付款时,当合约被调用成功,MTG 会把相应的资产转出到该用户的代理 Mixin 帐号,然后由代理帐号再把资产转账给相应的收款人。
对于用户来说,依然是掌握着自己原本的私钥,使用着自己熟悉的钱包,通过 MVM,可以同时管理一个跨链的 Mixin 钱包,从而连接整个 Mixin 生态。
参考文档
- MVM 开发文档:https://mvm.dev/
- MVM 跨链桥重要合约地址:https://bridge.mvm.dev/
- Mixin 开发者文档:https://developers.mixin.one/docs/api-overview
- Mixin 及 MVM 相关的 JS SDK:https://github.com/MixinNetwork/bot-api-nodejs-client
- MVM 官方跨链桥项目:https://bridge.mvm.app/
- MVM Bridge 项目源码:https://github.com/MixinNetwork/bridge.mvm.app
- 跨链桥项目 MVG:https://mvg.finance/
- Quill 项目源码:https://github.com/baizhiheizi/quill
- MixPay 开发者文档:https://mixpay.me/developers
最后
本文所有内容都是来自我个人的实践和理解,如有错漏,敬请斧正。
如果有任何其他疑问,也欢迎留言讨论。