0x-start-project源码学习 - 2

之前介绍了0x-start-project学习前需要只要的一些知识,现在我们来具体的学习0x-start-project
0x-start-project是对0x-project一些具体业务场景的模拟与实现,通过对0x-start-project源码的学习,可以更加便于我们了解0x-project的以业务流程以及具体代码的实现。

通过查看scenarios/all文件,可以发现0x-start-project主要模拟实现了如下场景:

  • fillOrderERC20();
  • fillOrderFees();
  • fillOrderERC721();
  • matchOrders();
  • executeTransaction();
  • executeTransactionCancelOrder();
  • cancelOrdersUpTo();
  • forwarder_buy_erc20_tokens();
  • forwarder_buy_erc721_tokens();
  • fillOrderMultiAsset();
  • dutchAuction();

它们对应了0x project不同的业务场景或交易策略。

在这些场景之外还提供了可以运行的SRA(standard relayer api)服务来模拟简单的Relayer的实现。

在开始运行项目之前,你需要使用0x project提供的ganache快照,来运行ganache-cli

1
2
yarn download_snapshot
yarn ganache-cli

我们本次会主要学习下面三个场景:

  • fillOrderERC20();
  • fillOrderFees();
  • fillOrderERC721();

它们都是以填写订单的场景,区别在于交易的币种以及是否具有交易费用。

基本都是如下步骤:

  1. 向0x进行对应交易币种授权,如含有交易费用需要对ZRX进行授权
  2. 卖方创建订单并进行签名
  3. 买方执行签名后订单并完成交易

订单在第3步之前都在链下进行,当执行订单时(fillOrder)数据才会上链,这时会返回对应的txHash,通过判断txHash来确认交易完成。

我们以对应的fillOrderFees代码为例来学习:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
PrintUtils.printScenario('Fill Order with Fees');
// Initialize the ContractWrappers, this provides helper functions around calling
// 0x contracts as well as ERC20/ERC721 token contracts on the blockchain
// 实例化contractWrappers
const contractWrappers = new ContractWrappers(providerEngine, { networkId: NETWORK_CONFIGS.networkId });
// Initialize the Web3Wrapper, this provides helper functions around fetching
// account information, balances, general contract logs
// 实例化web3Wrapper
// providerEngine确定了进行连接的网络
const web3Wrapper = new Web3Wrapper(providerEngine);
// 从ganache生成的10个地址中取前三个分别作为卖方地址,买方地址,中介费用接收地址
const [maker, taker, feeRecipient] = await web3Wrapper.getAvailableAddressesAsync();
// 获取ZRX代币合约地址
const zrxTokenAddress = contractAddresses.zrxToken;
// 获取WETH代币合约地址
const etherTokenAddress = contractAddresses.etherToken;
// PrintUtils为辅助的信息打印库
const printUtils = new PrintUtils(
web3Wrapper,
contractWrappers,
{ maker, taker, feeRecipient },
{ WETH: etherTokenAddress, ZRX: zrxTokenAddress },
);
// 输出账户信息
printUtils.printAccounts();
// the amount the maker is selling in maker asset
// toBaseUnitAmount为按精度转换金额,返回值为BigNumber
// 卖方出售金额
const makerAssetAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(5), DECIMALS);
// the amount the maker wants of taker asset
// 卖方支付金额
const takerAssetAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(0.1), DECIMALS);
// the amount of fees the maker pays in ZRX
// 卖方交易费用
const makerFee = Web3Wrapper.toBaseUnitAmount(new BigNumber(0.01), DECIMALS);
// the amount of fees the taker pays in ZRX
// 买方交易费用
const takerFee = Web3Wrapper.toBaseUnitAmount(new BigNumber(0.01), DECIMALS);
// 0x v2 uses hex encoded asset data strings to encode all the information needed to identify an asset
// 如果是ERC20,AssetData需要通过encodeERC20AssetData设置tokenaddress来确定币种
// 如果是ERC721,AssetData可以传入tokenaddress以及tokenid
// const makerAssetData = assetDataUtils.ecodeERC721AssetData(dummyERC721TokenContract.address, tokenId);
// 卖方卖出币种
const makerAssetData = assetDataUtils.encodeERC20AssetData(zrxTokenAddress);
// 买方卖出币种
const takerAssetData = assetDataUtils.encodeERC20AssetData(etherTokenAddress);
// 用于接收fillorder交易的Hash
let txHash;
// 用于接收fillorder交易的数据
let txReceipt;
// Approve the ERC20 Proxy to move ZRX for maker and taker
// 设置卖方交易授权
// 用于支付交易费用
const makerZRXApprovalTxHash = await contractWrappers.erc20Token.setUnlimitedProxyAllowanceAsync(
zrxTokenAddress,
maker,
);
await printUtils.awaitTransactionMinedSpinnerAsync('Maker ZRX Approval', makerZRXApprovalTxHash);
// 设置买方交易授权
// 用于支付交易费用
// ERC721需要使用contractWrappers.erc721Token.setProxyApprovalForAllAsync进行授权
const takerZRXApprovalTxHash = await contractWrappers.erc20Token.setUnlimitedProxyAllowanceAsync(
zrxTokenAddress,
taker,
);
await printUtils.awaitTransactionMinedSpinnerAsync('Taker ZRX Approval', takerZRXApprovalTxHash);
// Allow the 0x ERC20 Proxy to move WETH on behalf of takerAccount
// WETH账户授权
const takerWETHApprovalTxHash = await contractWrappers.erc20Token.setUnlimitedProxyAllowanceAsync(
etherTokenAddress,
taker,
);
await printUtils.awaitTransactionMinedSpinnerAsync('Taker WETH Approval', takerWETHApprovalTxHash);
// Convert ETH into WETH for taker by depositing ETH into the WETH contract
// 兑换WETH
const takerWETHDepositTxHash = await contractWrappers.etherToken.depositAsync(
etherTokenAddress,
takerAssetAmount,
taker,
);
await printUtils.awaitTransactionMinedSpinnerAsync('Taker WETH Deposit', takerWETHDepositTxHash);
// 打印操作信息
PrintUtils.printData('Setup', [
['Maker ZRX Approval', makerZRXApprovalTxHash],
['Taker ZRX Approval', takerZRXApprovalTxHash],
['Taker WETH Approval', takerWETHApprovalTxHash],
['Taker WETH Deposit', takerWETHDepositTxHash],
]);
// Set up the Order and fill it
// 生成订单过期时间
const randomExpiration = getRandomFutureDateInSeconds();
// 0x合约地址(用于交易)
const exchangeAddress = contractAddresses.exchange;
// Create the order
// 创建订单信息
const order: Order = {
exchangeAddress, // 0x合约地址
makerAddress: maker, // maker卖方地址
takerAddress: NULL_ADDRESS,// 买方地址
senderAddress: NULL_ADDRESS,// TODO: 应该是执行交易者的地址
feeRecipientAddress: feeRecipient,// 中介费用接收地址
expirationTimeSeconds: randomExpiration, // 过期时间
salt: generatePseudoRandomSalt(),//随机种子 - 盐
makerAssetAmount,// 卖方交易金额
takerAssetAmount,// 买方交易金额
makerAssetData,// 卖方交易币种
takerAssetData,// 买方交易币种
makerFee,// 卖方支付中介费用 为0代表无中介费用
takerFee,// 买方支付中介费用
};
// 打印订单信息
printUtils.printOrder(order);
// Print out the Balances and Allowances
// 打印授权信息
await printUtils.fetchAndPrintContractAllowancesAsync();
// 打印账户余额信息
await printUtils.fetchAndPrintContractBalancesAsync();
// Generate the order hash and sign it
// 获取订单HASH
const orderHashHex = orderHashUtils.getOrderHashHex(order);
// 进行订单签名
const signature = await signatureUtils.ecSignHashAsync(providerEngine, orderHashHex, maker);
// 签名后的订单,多出了signature字段
const signedOrder = { ...order, signature };
// Fill the Order via 0x Exchange contract
// 执行订单交易
txHash = await contractWrappers.exchange.fillOrderAsync(signedOrder, takerAssetAmount, taker, {
gasLimit: TX_DEFAULTS.gas,
});
// 等待交易完成获取交易数据
txReceipt = await printUtils.awaitTransactionMinedSpinnerAsync('fillOrder', txHash);
// 打印交易信息
printUtils.printTransaction('fillOrder', txReceipt, [
['orderHash', orderHashHex],
['takerAssetAmount', takerAssetAmount.toString()],
]);
// Print the Balances
// 打印余额信息
await printUtils.fetchAndPrintContractBalancesAsync();
// Stop the Provider Engine
providerEngine.stop();

Web3Wrapper是的以太坊相关的工具库,用于相关的工具操作。

ContractWrappers是合约相关的操作库,进行如ERC20,ERC721,WETH等合约相关的操作。

其中contractWrappers.exchange是和0x相关交易的操作