什么是0x协议,它的工作机制是怎样的?在这个教程中,我们将介绍0x协议的基本概念,例如其链下订单中继、去中心化交易中继器等,学习如何使用0x智能合约在以太坊公链或私链上实现自己的去中心化交易所(DEX),掌握如何利用0x.js实现0x资产交易委托订单的链下创建与签名、链上验证及执行。如果你是Go工程师,可以使用这个 0x协议Golang开发包 - ZrxTool 。
用自己熟悉的语言学习 以太坊DApp开发 :
Java | Php | Python | .Net / C# | Golang | Node.JS | Flutter / Dart
0x是一种开放协议,支持以太坊区块链上点对点的资产交换。其主要特性为:
0x协议使用模块化方式在以太坊区块链上交易资产,其主要优势在于:
0x协议可用于多种用例,例如游戏和收藏品,预测市场,去中心化交易的订单簿,去中心化贷款等。同时,0x使用以太坊区块链的模块化智能合约,可以通过治理进行升级,而不会影响系统的其他组件,也不会引起活跃市场的中断。
利用0x协议所提供的开源基础架构,开发人员能够轻松构建支持ERC-20和ERC-721资产的去中心化交易所。
0x协议的智能合约主要包括以下类别:
为了部署、使用0x协议的智能合约,我们需要先安装0x.js。0x.js是一个与0x协议进行交互的JavaScript库,利用它就可以轻松地调用0x协议的智能合约来创建、取消或验证订单,或者检查ERC20和ERC721资产持有者的授权额度和余额。
0x协议采用链下订单中继、链上结算的模式,密码学签名的订单可以通过任意通信渠道在区块链之外传播。感兴趣的对手方可以将这些订单中的一个或多个注入到0x的资产交换合约中,在区块链上执行和结算交易。
可以使用0x协议来交换任何ERC20或ERC721资产。上图显示了当Taker向0x资产交换智能合约
提交订单时资产转移的实际处理流程:
fillOrder()
方法提交签名订单transferFrom()
方法transferFrom()
方法要与智能合约进行交互,我们需要部署0x智能合约,并使用智能合约的地址通过0x.js库与智能合约进行交互。
首先安装0x.js:
Use npm install 0x.js — save to install and save 0x.js library
资产交换合约:利用源代码 部署交换智能合约,其中交换合约的构造函数不需要任何参数,智能合约的部署者(msg.sender)将是智能合约的所有者。所有者将能够在交换合约中设置assetProxy合约的地址。
ERC20资产代理合约:利用源代码 部署ERC20proxy合约,其中代理合约的构造函数不需要任何参数,智能合约的部署者(msg.sender)将是智能合约的所有者。所有者将能够在ERC20Proxy合约中设置资产交换合约的地址。
ERC721资产代理合约:利用源代码 部署ERC20proxy合约,其中代理合约的构造函数不需要任何参数,智能合约的部署者(msg.sender)将是智能合约的所有者。所有者将能够在ERC20Proxy合约中设置交换合约的地址。
部署以上合约后,需要在交换合约中设置资产代理合约的地址,在资产代理合约中设置交换合约的地址。
调用资产交换合约的registerAssetProxy(assetProxyAddress)
方法将存储资产代理合约的地址,在该地址将发生实际交易以交换代币资产。该方法只能由资产交换智能合约的所有者调用。
调用ERC20Proxy合约的addAuthorizedAddress(exchangeAddress)
方法注册交换合约。调用ERC20Proxy合约的removeAuthorizedAddress(exchangeAddress)
方法删除交换合约。
使用资产交换合约和资产代理合约的地址通过0x.js库进行交互:
let contractConfig = {
contractAddresses: {
erc20Proxy: proxyAddress.toLowerCase(),
erc721Proxy: "0x1d7022f5b17d2f8b695918fb48fa1089c9f85401",
exchange: exchangeAddress.toLowerCase()
},
networkId: networkId
};
const contractWrappers = new ContractWrappers(holderEngine, contractConfig);
现在就可以交互部署在专用或测试网络上的0x协议智能合约。请记住添加RPC子提供程序以与区块链进行交互。
为了与0x.js库进行交互,我们需要导入相关的软件包,如下所示,最终目标是使用0x.js库用Maker帐户创建订单,并且Taker将使用fillOrder()
方法提交执行交易:
const {
assetDataUtils,BigNumber,ContractWrappers,
generatePseudoRandomSalt,orderHashUtils,signatureUtils
} = require('0x.js');
const TX_DEFAULTS = { gas: 400000 };
const { RPCSubprovider, Web3ProviderEngine } = require('0x.js');
let newWallet = new ethers.Wallet(wallet.signingKey.privateKey, prov);
const holderWallet = new PrivateKeyWalletSubprovider(wallet.signingKey.privateKey.slice(2));
添加RPC子提供程序:
const holderEngine = new Web3ProviderEngine();
holderEngine.addProvider(holderWallet);
holderEngine.addProvider(new RPCSubprovider(providerUrl));
holderEngine.start();
在RPC子提供程序中,可以使用自定义URL分别连接以太坊主网、测试网或私有区块链的区块链连接。
获取0x合约地址并实例化合约包装器:
const contractWrappers = new ContractWrappers(holderEngine, contractConfig);
const web3Wrapper = new Web3Wrapper(providerEngine);
const contractAddresses = getContractAddressesForNetworkOrThrow(100);//networkID
现在,Maker将创建订单,而Taker将执行订单以交换Maker的资产:
const tokenAAddress = contractAddresses.tokenA;
const tokenBAddress = contractAddresses.tokenB;
const exchange = contractAddresses.exchange;
所有地址都可以从0x.js库获取到。
const makerAssetData = assetDataUtils.encodeERC20AssetData(tokenAAddress);
const takerAssetData = assetDataUtils.encodeERC20AssetData(tokenBAddress);
const makerAssetAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(100), DECIMALS);
const takerAssetAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(200), DECIMALS);
const NULL_ADDRESS = '0x0000000000000000000000000000000000000000';
const ZERO = new BigNumber(0);
const DECIMALS = 18;
现在,Maker和Taker应该授权相应的资产代理合约,以分别代表Maker和Taker转让代币资产:
//Allow ERC20 Proxy to move tokenA on behalf of makerAccount
const makerApprovalTxHash = await contractWrappers.erc20Token.setUnlimitedProxyAllowanceAsync(
tokenAAddress,
maker,
);
await web3Wrapper.awaitTransactionSuccessAsync(makerApprovalTxHash);
// Allow ERC20 Proxy to move tokenB on behalf of takerAccount
const takerApprovalTxHash = await contractWrappers.erc20Token.setUnlimitedProxyAllowanceAsync(
tokenBAddress,
taker,
);
await web3Wrapper.awaitTransactionSuccessAsync(takerApprovalTxHash);
在Maker和Taker批准资产代理合约之后,代理合约就可以分别代表Maker和Taker转让代币资产。现在,Maker将创建一个委托订单并在链下签名,而Taker将在链上执行订单。
创建订单:
const order = {
exchangeAddress: exchangeAddress,
makerAddress: maker,//address of maker
takerAddress: taker,//address of taker
senderAddress: taker,//address of sender
feeRecipientAddress: NULL_ADDRESS,//fee in the form of native currency of platform
expirationTimeSeconds: randomExpiration,//expire time of order
salt: generatePseudoRandomSalt(),//random no to differentiate order
makerAssetAmount,//maker asset amount
takerAssetAmount,//taker asset amount
makerAssetData,//encoded address of tokenA
takerAssetData,//encoded address of tokenB
makerFee: ZERO,//fee if required
takerFee: ZERO,//fee if required
};
现在我们创建了一个资产交换委托订单。接下来在调用0x.js库的getOrderHash()
函数获得订单哈希值以便进行签名。根据EIP712对订单计算哈希:
const orderHashHex = orderHashUtils.getOrderHashHex(order);
获取订单的哈希后,Maker使用0x.js库的ecSignHashAsync()
方法签名订单。
const signature = await signatureUtils.ecSignHashAsync(providerEngine, orderHashHex, maker);
const signedOrder = {
…order,
signature
};
Taker可以使用资产交易合约的validateFillOrder方法验证订单是否可以执行:
await contractWrappers.exchange.validateFillOrderThrowIfInvalidAsync(
signedOrder,
takerAssetAmount,
taker
);
Taker最终调用资产交易合约的fillOrder方法执行订单:
try{
txHash = await contractWrappers.exchange.fillOrderAsync(
signedOrder,
takerAssetAmount,
taker,
{TX_DEFAULTS,}
);
var transaction = await web3Wrapper.awaitTransactionSuccessAsync(txHash);
}catch(error){}
原文链接:0x协议对接开发教程 — 汇智网