去中心化钱包的交易设计
一、交易
下图是MyEtherWallet里面的钱包应用和账户(一组公私钥)的关系图
- 签名方法(WalletInterface/signTransaction):
带私钥的钱包应用的签名方法
import { Transaction } from '@ethereumjs/tx'; let tx = Transaction.fromTxData(txParams); tx = tx.sign(this.privateKey); // 这样签名的前提是,钱包应用有管理到私钥(比如助记词钱包、私钥钱包,但不包括walletConnect钱包)
无私钥方法(HybridWallet/HDWallet里面signTransaction,当isPrivKey为false的逻辑): 要用外部client的方法
- nouce
要从链上获取最新的nouce值
const Web3 = require('web3'); const web3 = new Web3('https://mainnet.infura.io/v3/YOUR_INFURA_API_KEY'); // 钱包应用关联的infura节点 web3.eth.getTransactionCount(address);
- gasPrice
一般会设置几种选项(经济型、常规、更快)
// 经济型: 从当前以太坊网络获取得到 gasPrice = await web3.eth.getGasPrice(); // 常规型 if (gasPrice > LIMITER) { let initialValue = BigNumber(gasPrice).times(MED_MULTIPLIER); initialValue = initialValue.plus(MED_CONST); return BigNumber(initialValue).toFixed(0); } return BigNumber(gasPrice).times(1.25).toFixed(0); // 更快型 if (gasPrice > LIMITER) { let initialValue = BigNumber(gasPrice).times(FAST_MULTIPLIER); initialValue = initialValue.plus(FAST_CONST); return BigNumber(initialValue).toFixed(0); } return BigNumber(gasPrice).times(1.5).toFixed(0);
二、购买币
基本流程是:
- 前端从后台获取到全部的购买服务供应商信息, 包括名称、支持的法币列表、支持的法币->token购买列表;
- 用户在前端先选择要支付法币、要花费的金额、购入token存放地址;
- 确定后,转换到列表展示每个供应商可买入的token数量、费用等
要维护的供应商信息包括有
{ 0: { name: 'MOONPAY', // token和法币的汇率 prices: [ { crypto_currency: 'ETH', fiat_currency: 'USD', price: '3379.08322' }, ... ], // 支持的付款法币 fiat_currencies: ['USD', 'EUR', ...], // 限额 limits: [ { fiat_currency: 'USD', limit: { min: 30, max: 5000 } }, ... ], // 法币间汇率 conversion_rates: [ { fiat_currency: 'USD', exchange_rate: 1 }, ... ] }, 1: { name: 'SIMPLEX', ...... }, ... }
购买时具体需要信息(也要传给服务供应商的)包括:
const buyObj = { cryptoToFiat: this.moonpayCryptoAmount, // token数量 selectedCryptoName: this.selectedCryptoName, // token名 plusFeeF: this.plusFeeF, // 法币金额 includesFeeText: this.includesFeeText, // 费用 networkFeeText: this.networkFeeText, dailyLimit: this.dailyLimit, monthlyLimit: this.monthlyLimit, fiatAmount: this.amount }; this.$emit('success', [ this.simplexQuote, this.toAddress, // 买入token存放地址 buyObj, 1, this.selectedCurrency, // token信息 this.selectedFiat // 法币信息 ]);
三、兑换
基本流程是:
- 后端维护能参与兑换的token列表;
- 用户进入兑换页,前端查询,分类展示成当前链token,跨链token;
- 用户选择fromToken的类型、数量、toToken的类型,自动发请求到后台(供应商)查询后,以readonly形式展示toToken的数量
- 选择fromAddress和toAddress(数据来源于addressBookStore,额外应该有地址管理功能,可以增加保存自己的或者其他人的地址)
- 展示服务供应商列表供用户选择,包括具体信息(汇率、费用等)
- 用于选择交易提供商provider之后,自动调用供应商的getTrade方法,会得到1个trade结构数据,该结构数据用于构造tx。 返回trade结构里最重要的字段是data,它一个十六进制字符串,包含了调用智能合约方法的数据。
- 获取nouce,给交易签名signTransaction后,调用sendTransaction
第3步里兑换token数量查询依赖的数据
// 返回toToken的数量、汇率等 getQuote({ fromContractAddress: fromAddress, toContractAddress: toAddress, amount: queryAmount.toFixed(fromT.decimals), chain: this.chain, // 链地址 excludeDexes: Object.values(MEWPClass.supportedDexes) .filter(dex => dex !== this.provider) .join(',') // 要排除的交易所 }};
第6步里获取交易结构数据的请求参数
{
address: fromAddress,
recipient: toAddress,
dex: this.provider,
exchange: quote.exchange,
platform: 'web',
fromContractAddress: contactFromAddress,
toContractAddress: contractToAddress,
amount: queryAmount.toFixed(fromT.decimals),
chain: this.chain
}
四、转账
五、NFT交易
基本流程:
- 构造NFT得交易数据结构
- 发送交易数据: 获取nouce、签名、发送
send(to, token, gasPrice = undefined) { let raw; this.contract = new this.web3.eth.Contract(token.erc721 ? ABI : ERC1155ABI); if (token.contract.includes(configs.cryptoKittiesContract)) { raw = this.cryptoKittiesTransfer(to, token); } else if (token.erc721) { raw = this.safeTransferFrom(to, token); } else { raw = this.safeTransferFromRarible(to, token, 1); } raw.from = this.address; if (gasPrice !== undefined) raw.gasPrice = gasPrice; return this.web3.eth.sendTransaction(raw); } safeTransferFrom(to, token) { return { to: token.contract, // 交易data的内容是,调用合约的转移NFT的方法 data: this.contract.methods .safeTransferFrom(this.address, to, token.token_id) .encodeABI() }; }
五、跨链交易