0x protocol分析之take Limit Order
关于0x是什么的问题这里就不再啰嗦,基本的概念有很多解释的文档,这里主要展示了limit order 的挂单和吃单流程。
0xprotocol的流程概述
0x中一些问题的解释
0x 限价单挂单吃单流程
0x的市价单的交互官方文档写的比较清楚,操作起来也比较简单 。这里主要记录一下限价单的挂单和吃单。
挂单
- 按格式准备一个订单
OX中限价的订单的数据结构
struct LimitOrder {
IERC20TokenV06 makerToken;
IERC20TokenV06 takerToken;
uint128 makerAmount;
uint128 takerAmount;
uint128 takerTokenFeeAmount;
address maker;
address taker;
address sender;
address feeRecipient;
bytes32 pool;
uint64 expiry;
uint256 salt;
}
const order = new utils.LimitOrder({
"makerToken": "0xc778417e063141139fce010982780140aa0cd5ab",
"takerToken": "0xf8db576976096d5f19c21ab12115d7a2aa7c77cc",
"makerAmount": "1",
"takerAmount": "1000000000000000",
"maker": "0xD993d1049bd38eDBe12f8F6c9cdB716970B778D2",
"taker": "0x0000000000000000000000000000000000000000",
"pool": "0x0000000000000000000000000000000000000000000000000000000000000000",
"expiry": "1627240945",
"salt": "1626940945557",
"chainId": 3,
"verifyingContract": "0xdef1c0ded9bec7f1a1670819833240f027b25eff",
"takerTokenFeeAmount": "0",
"sender": "0x0000000000000000000000000000000000000000",
"feeRecipient": "0x0000000000000000000000000000000000000000",
"signature":{
"v": 28,
"r": "0x921d18a7b4eaf9ce5227031f872dd54a73237e5eb90d81da3da05465f16f9b4f",
"s": "0x524904cac050c9ae8f5999cc0f96415d1422dc5b36bb7fbe11d8522cc6b28365",
"signatureType": 2
}
});
其中 signature 字段是订单的签名,签名的创建有多种方式
其一是通过 @0x/protocol-utils提供的工具签名 文档地址
yarn add @0x/protocol-utils
var Web3 = require('web3');
const utils = require('@0x/protocol-utils');
const contractAddresses = require("@0x/contract-addresses");
var infuranKey = "";
var web3 = new Web3("https://ropsten.infura.io/v3/"+infuranKey);
const privateKey = "私钥"
const maker = "0xcC2847AB347A4752a233e20b7E4410e138f096F6";
const makeToken= "0xc778417e063141139fce010982780140aa0cd5ab"
const takeToken= "0xf8db576976096d5f19c21ab12115d7a2aa7c77cc";
const makerAmount = "1000"
const takerAmount = "1000"
const CHAIN_ID = 3;
const NULL_ADDRESS = "0x0000000000000000000000000000000000000000";
const addresses = contractAddresses.getContractAddressesForChainOrThrow(
CHAIN_ID
);
main()
.then(() => process.exit(0))
.catch(error => {
console.error(error);
process.exit(1);
});
async function main() {
await sign();
}
async function sign() {
console.log(`CHAIN_ID = ${CHAIN_ID}, ETH = ${addresses.etherToken}, exchange = ${addresses.exchangeProxy}`);
// 订单组装
const order = new utils.LimitOrder({ // or utils.RfqOrder
makerToken : makeToken,
takerToken : takeToken,
makerAmount: makerAmount,
takerAmount: takerAmount,
maker: maker,
taker: NULL_ADDRESS,
pool: "0x0000000000000000000000000000000000000000000000000000000000000000",
expiry: "2014956123",
salt: "6",
chainId: CHAIN_ID,
verifyingContract: `${addresses.exchangeProxy}`,
takerTokenFeeAmount: "0",
sender: NULL_ADDRESS,
feeRecipient: NULL_ADDRESS,
});
// 签名
const signature = await order.getSignatureWithKey(privateKey, utils.SignatureType.EIP712);
console.log(signature)
if(await postOrder(order,signature)){
console.log("发布成功")
return;
}
console.log("发布失败")
}
// 发布订单
async function postOrder(order,signature) {
const signedOrder = { ...order, signature };
// console.log(signedOrder);
const resp = await fetch("https://ropsten.api.0x.org/sra/v4/order", {
method: "POST",
body: JSON.stringify(signedOrder),
headers: {
"Content-Type": "application/json"
}
});
if (resp.status === 200) {
console.log("Successfully posted order to SRA");
return true;
} else {
const body = await resp.json();
console.log(
`ERROR(status code ${resp.status}): ${JSON.stringify(body, undefined, 2)}`
);
return false;
}
}
通过另外一种方式,提供的工具做的签名可以完成挂单
sign地址
吃单
- maker 要把maketToken approve 给[0x相关的合约] 0xFbB2a5B444aA89a4054883A4D23FBA45BFc6840A
- taker也要 takerToken approve 给合约
- 注意take吃单要支付 70k*gasPrice的protocol fee
- 0x 合约中一些方法的解析
orderParam 订单参数
["0xf8db576976096d5f19c21ab12115d7a2aa7c77cc","0xc778417e063141139fce010982780140aa0cd5ab",1000,1,0,"0xcc2847ab347a4752a233e20b7e4410e138f096f6","0x0000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000",2014956256,0]
signParam 签名参数
[2,28,"0xd447dc3ef38cf18e82e34e2bffb78d2c8e7175881ae6b881a005a956b0f964ba","0x2a2b6dfd811a90d1dd72b830f09f9dcd6db2f9224b343dd4d4d8b33543c24cf8"]
- getLimitOrderHash(orderParam) 获取限价单的订单hash
return orderHash - getLimitOrderInfo(orderParam) 获取限价的信息
返回的是一个结构体,分别是订单hash,订单状态,已经订单中已经被吃掉的数量
其中订单的状态分别有以下几种return struct OrderInfo { bytes32 orderHash; OrderStatus status; uint128 takerTokenFilledAmount; }
enum OrderStatus { INVALID, FILLABLE, FILLED, CANCELLED, EXPIRED }
可以购买的订单的状态是 1 FILLABLE,购买完了的是 2 FILLED
-
getLimitOrderRelevantState(orderParam,signParam) 这方法是获取限价单的一些状态, 这个会展示出当前的订单还有多少数量可以买的
返回有三个参数 分别是
OrderInfo的结构体,这个上面已经提过
actualFillableTakerTokenAmount 实际可以购买的数量,
isSignatureValid 挂单者的签名是否正确
其中 实际actualFillableTakerTokenAmount>0,isSignatureValid=true才可以购买成功. -
fillLimitOrder(orderParam,signParam,takerAmount) 吃单
上述的查询可以说都是为了这个方法的填入正确的参数做准备。
检查完上面的状态都没问题,将订单参数呵呵签名参数填入,再填上一个合适的takerAmount的数量(<=actualFillableTakerTokenAmount)
另外注意发送交易的时候 要支付 70K*gasPrice的protocol费。
正常情况下发送交易,就可以完成吃单操作。
OK,以上就是使用0x 协议完成的挂单吃单的流程。
如果想详细分析token的流转,可以分析下面这个
吃单的一个交易hash
如果有问题,可以留言交流。