【0x】 0x.js入门
今天我们来介绍一个以太坊的一个智能合约(不太懂智能合约的可以自行百度)0x。
0x协议是一个去中心化的交易智能合约,它可以实现币种与币种之间的定价交易。
实现0x协议的交易所有DDEX(https://www.ddex.io/trade/DAI-ETH),radarrelay(https://app.radarrelay.com/DAI/WETH)
(绝对不是打广告,纯粹学习)
https://0xproject.com/pdfs/0x_white_paper.pdf
上面是0x的白皮书,0x团队也出了自己的代币:ZRX
0x的文档
https://0xproject.com/docs/0x.js
0x的API文档
https://chat.0xproject.com/channel/general
0x的开发团队的聊天室,里面的大佬会回答你的问题,有问题可以直接去问他们。
我将作为一个纯客户的角度来讲解一下,如何利用代码来实现自动挂单和填单。
因为0x的工作包是js写的,所以没办法只能使用nodejs去去做这个事。
ddex_submitOrder.js
let ethereumjsutil = require("ethereumjs-util"); let hashPersonalMessage = ethereumjsutil.hashPersonalMessage; let ecsign = ethereumjsutil.ecsign; let toRpcSig = ethereumjsutil.toRpcSig; let toBuffer = ethereumjsutil.toBuffer; let privateToAddress = ethereumjsutil.privateToAddress; async function post(data, options) { let https = require('https'); let result = ""; return new Promise(function (resolve, reject) { let req = https.request(options, function (res) { res.setEncoding('utf8'); res.on('data', function (chunk) { result += chunk; }); res.on('end', function () { resolve(result); }); }); req.on('error', (e) => { reject(e); }); let json = JSON.stringify(data); req.write(json); req.end(); }); } /** * * @param privateKey * @param amount * @param price * @param side * @param marketId * @returns {Promise<string>} */ async function submitDdexOrder(privateKey,amount,price,side,marketId) { try{ let message = "HYDRO-AUTHENTICATION@" + new Date().getTime(); let address = "0x" + privateToAddress(privateKey).toString("hex"); let shaAuth = hashPersonalMessage(toBuffer(message)) let ecdsaSignatureAuth = ecsign(shaAuth, toBuffer(privateKey)) let signatureAuth = toRpcSig(ecdsaSignatureAuth.v, ecdsaSignatureAuth.r, ecdsaSignatureAuth.s) let ddexSignature = address + '#' + message + '#' + signatureAuth let buildOptions = { host: 'api.ddex.io', port: 443, path: '/v2/orders/build', method: 'POST', headers: { 'Content-Type': 'application/json', 'Hydro-Authentication': ddexSignature } }; let buildData = { amount: amount, price: price, side: side, marketId: marketId }; let orderResult = await post(buildData, buildOptions); let orderJson = JSON.parse(orderResult); if (orderResult && orderJson.status === 0) { //给我未签名的订单签名 let unsignOrder = orderJson.data.order; let orderId = unsignOrder.id; let shaOrder = hashPersonalMessage(toBuffer(orderId)); let ecdsaSignatureOrder = ecsign(shaOrder, toBuffer(privateKey)); let signatureOrder = toRpcSig(ecdsaSignatureOrder.v, ecdsaSignatureOrder.r, ecdsaSignatureOrder.s); //提交订单的请求地址 let orderOptions = { host: 'api.ddex.io', port: 443, path: '/v2/orders', method: 'POST', headers: { 'Content-Type': 'application/json', 'Hydro-Authentication': ddexSignature } }; let orderReq = { orderId: orderId, signature: signatureOrder }; let placeOrderResult = await post(orderReq, orderOptions); let placeOrderJson = JSON.parse(placeOrderResult); if(placeOrderResult && placeOrderJson.status === 0){ return orderId; }else{ throw new Error(placeOrderResult); } } else { throw new Error(orderResult); } }catch (e){ throw e; } return ""; } module.exports = { submitDdexOrder }
radarrelay_fillOrder.js
var Zerojs = require("0x.js"); var HDWalletProvider = require("truffle-hdwallet-provider"); var connnect = require("@0xproject/connect"); var HttpClient = connnect.HttpClient; var ZeroEx = Zerojs.ZeroEx; var BigNumber = require("@0xproject/utils").BigNumber; /** * fillOrder * @param mnemonic wallet mnemonic * @param clientUrl geth client node * @param relayerApiUrl relayerApiUrl eg:https://api.radarrelay.com/0x/v0/ * @param baseTokenAddress the token you have * @param quoteTokenAddress the token address you want to transfer * @param fillOrderAmount how much base token you want to fill * @returns {Promise<string>} */ async function fillOrder(mnemonic, clientUrl, relayerApiUrl, baseTokenAddress, quoteTokenAddress, networkId,fillOrderAmount) { return new Promise(async function (resolve, reject) { try { let provider = new HDWalletProvider(mnemonic, clientUrl); let configs = { networkId: networkId, }; let zeroEx = new ZeroEx(provider, configs); let accounts = await zeroEx.getAvailableAddressesAsync(); let makerAddress = accounts[0]; let relayerClient = new HttpClient(relayerApiUrl); let DECIMALS = 18; let orderbookRequest = { baseTokenAddress: baseTokenAddress, quoteTokenAddress: quoteTokenAddress }; let orderbookResponse = await relayerClient.getOrderbookAsync(orderbookRequest); let sortedBids = orderbookResponse.bids.sort((orderA, orderB) => { const orderRateA = new BigNumber(orderA.takerTokenAmount).div(new BigNumber(orderA.makerTokenAmount)); const orderRateB = new BigNumber(orderB.takerTokenAmount).div(new BigNumber(orderB.makerTokenAmount)); return orderRateB.comparedTo(orderRateA); }); let foa = ZeroEx.toBaseUnitAmount(new BigNumber(fillOrderAmount), DECIMALS); let bidToFill = sortedBids[sortedBids.length - 1]; let fillTxHash = await zeroEx.exchange.fillOrderAsync(bidToFill, foa, true, makerAddress); let result = await zeroEx.awaitTransactionMinedAsync(fillTxHash, 1000, 1800000); if (result.logs) { await zeroEx.exchange.throwLogErrorsAsErrors(result.logs); } resolve(fillTxHash); } catch (e) { reject(e); } }); } /** * ETH WETH 1:1 * @param mnemonic wallet mnemonic * @param wethTokenAddress the addres of weth * @param ethAmount how much eth you want to exchange * @returns {Promise<string>} */ async function exchangeWETH(mnemonic, clientUrl, wethTokenAddress, ethAmount, networkId) { return new Promise(async function (resolve, reject) { try { let provider = new HDWalletProvider(mnemonic, clientUrl); let configs = { networkId: networkId, }; let zeroEx = new ZeroEx(provider, configs); let DECIMALS = 18; //ETH WETH let accounts = await zeroEx.getAvailableAddressesAsync(); let makerAddress = accounts[0]; const ethToConvert = ZeroEx.toBaseUnitAmount(new BigNumber(ethAmount), DECIMALS); let depositTxHash = await zeroEx.etherToken.depositAsync(wethTokenAddress, ethToConvert, makerAddress); let result = await zeroEx.awaitTransactionMinedAsync(depositTxHash, 1000, 1800000); if (result.logs) { await zeroEx.exchange.throwLogErrorsAsErrors(result.logs); } resolve(depositTxHash); } catch (e) { reject(e); } }); } module.exports = { fillOrder, exchangeWETH }
radarrelay_submitOrder.js
var Zerojs = require("0x.js"); var HDWalletProvider = require("truffle-hdwallet-provider"); var connnect = require("@0xproject/connect"); var HttpClient = connnect.HttpClient; var ZeroEx = Zerojs.ZeroEx; var BigNumber = require("@0xproject/utils").BigNumber; /** * * @param mnemonic wallet mnemonic * @param clientUrl geth client node * @param relayerApiUrl relayerApiUrl eg:https://api.radarrelay.com/0x/v0/ * @param makerTokenAddress the token you have * @param takerTokenAddress the token address you want to buy * @param makerTokenAmount how much token you want to sell * @param takerTokenAmount how much token you want to buy * @param unixExpireTime ExpireTime unix timestamps * @returns {Promise<string>} */ async function submitOrder(mnemonic, clientUrl, relayerApiUrl, makerTokenAddress, takerTokenAddress, makerTokenAmount, takerTokenAmount, unixExpireTime, networkId) { return new Promise(async function (resolve, reject) { try { let provider = new HDWalletProvider(mnemonic, clientUrl); let configs = { networkId: networkId, }; let zeroEx = new ZeroEx(provider, configs); let relayerClient = new HttpClient(relayerApiUrl); let EXCHANGE_ADDRESS = await zeroEx.exchange.getContractAddress(); let DECIMALS = 18; let addresses = await zeroEx.getAvailableAddressesAsync(); let ownerAddress = addresses[0]; let mta = ZeroEx.toBaseUnitAmount(new BigNumber(makerTokenAmount), DECIMALS); let tta = ZeroEx.toBaseUnitAmount(new BigNumber(takerTokenAmount), DECIMALS); const feesRequest = { exchangeContractAddress: EXCHANGE_ADDRESS, maker: ownerAddress, taker: ZeroEx.NULL_ADDRESS, makerTokenAddress: makerTokenAddress, takerTokenAddress: takerTokenAddress, makerTokenAmount:mta, takerTokenAmount:tta, expirationUnixTimestampSec: new BigNumber(unixExpireTime), salt: ZeroEx.generatePseudoRandomSalt(), }; let feesResponse = await relayerClient.getFeesAsync(feesRequest); let order = { ...feesRequest, ...feesResponse, }; let orderHash = ZeroEx.getOrderHashHex(order); let ecSignature = await zeroEx.signOrderHashAsync(orderHash, ownerAddress, false); let signedOrder = { ...order, ecSignature, }; await relayerClient.submitOrderAsync(signedOrder); resolve(orderHash); } catch (e) { reject(e); } }); } module.exports = { submitOrder }
test.js
let mnemonic = "xxx xxx xxxx xxxxxxx xxxxx"; let wethContractAddress = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"; let relayerApiUrl = "https://api.radarrelay.com/0x/v0/"; let WETH_ADDRESS = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"; let DAI_ADDRESS = "0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359"; let clientUrl = "https://mainnet.infura.io/xxxxxxxxxxx"; let privateKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; async function testExchangeWeth(){ try{ let dtx = await exchangeWETH(mnemonic,clientUrl,wethContractAddress,"0.001",1); console.info(dtx); }catch (e){ console.error(e); } } async function testFillRadarOrder(){ try{ let fillOrderAmount = "0.005"; let net = 1; let dtx = await require("./radarrelay_fillOrder").fillOrder(mnemonic,clientUrl,relayerApiUrl,WETH_ADDRESS,DAI_ADDRESS,net,fillOrderAmount); console.info(dtx); }catch (e){ console.error(e); } } async function testSubmitOrder(){ try{ let dtx = await require("./radarrelay_submitOrder").submitOrder(mnemonic,clientUrl,relayerApiUrl,WETH_ADDRESS,DAI_ADDRESS,0.001,50,1525513767,1); console.info(dtx); }catch (e){ console.error(e); } } async function testDdexOrder(){ try{ let orderId = await require("./ddex_submitOrder").submitDdexOrder(privateKey,5,0.0013184,"buy","DAI-ETH"); console.info(dtx); }catch (e){ console.error(e); } }
注意
1、选择的HDWalletProvider一定要选0x团队改过的。官网版本不能用,官方签名是不合法的。
2、选择好正确的网络ID和网络地址
3、我这里的都是生产环境的地址,测试环境的币种合约地址自己找,请做实验的时候再次确认几个币种的合约地址是否正确。
4、order整体结构就是这样,要仔细核对里面的参数是否输入正确
5、如果验签通过了,证明是合法订单