0x协议对接开发教程【DEX】

什么是0x协议,它的工作机制是怎样的?在这个教程中,我们将介绍 0x协议的基本概念,例如其链下订单中继、去中心化交易中继器等, 学习如何使用0x智能合约在以太坊公链或私链上实现自己的去中心化交易所(DEX), 掌握如何利用0x.js实现0x资产交易委托订单的链下创建与签名、链上验证及执行。

用自己熟悉的语言学习 以太坊DApp开发Java | Php | Python | .Net / C# | Golang | Node.JS | Flutter / Dart

1、什么是0x协议

0x是一种开放协议,支持以太坊区块链上点对点的资产交换。其主要特性为:

  • 安全的非托管交易:无需存入或从中心化平台取出,就可以直接实现钱包对钱包的资产交易。
  • 灵活的订单类型:支持立即购买,或允许潜在买家提交出价。
  • 有助于建立可持续的业务:每笔交易可以收取服务费,有助于生态的扩展

0x协议使用模块化方式在以太坊区块链上交易资产,其主要优势在于:

  • 可靠的智能合约:0x协议的智能合约已通过两轮严格的安全审核。
  • 可扩展架构:0x的模块化流水线支持开发者在通过扩展API插入自己的智能合约。
  • 高效设计:0x的具有链上结算功能的链下订单中继是一种节省手续费的P2P方式的委托单交换方法。

0x协议可用于多种用例,例如游戏和收藏品,预测市场,去中心化交易的订单簿, 去中心化贷款等。同时,0x使用以太坊区块链的模块化智能合约,可以通过治理进行升级, 而不会影响系统的其他组件,也不会引起活跃市场的中断。

利用0x协议所提供的开源基础架构,开发人员能够轻松构建支持ERC-20和ERC-721 资产的去中心化交易所。

2、0x协议的智能合约

0x协议的智能合约主要包括以下类别:

  • 资产交换合约:包含0x协议的业务逻辑,提供订单执行、订单取消、交易执行、签名验证、新资产注册等功能
  • ERC20资产代理合约:代表用户转让ERC20资产。因此,每个用户都需要授权该合约可以操作自己持有的ERC20代币。
  • ERC721资产代理合约:代表用户转让ERC721资产。因此,每个用户都需要授权该合约可以操作自己持有的ERC721资产

为了部署、使用0x协议的智能合约,我们需要先安装0x.js。0x.js是一个与0x协议进行交互的 JavaScript库,利用它就可以轻松地调用0x协议的智能合约来创建、取消或验证订单,或者检查 ERC20和ERC721资产持有者的授权额度和余额。

0x协议采用链下订单中继、链上结算的模式,密码学签名的订单可以通过任意通信渠道在区块链之外传播。 感兴趣的对手方可以将这些订单中的一个或多个注入到0x的资产交换合约中,在区块链上执行和结算交易。

可以使用0x协议来交换任何ERC20或ERC721资产。上图显示了当Taker向0x资产交换智能合约 提交订单时资产转移的实际处理流程:

  1. Taker调用资产交换智能合约的fillOrder()方法提交签名订单
  2. 资产交换合约将订单传递给相应的ERC20Proxy资产代理合约,实际的资产转移是在代理合约上进行的。 注意:Maker和Taker必须先授权ERC20Proxy合约,然后才能提交订单。
  3. 在资产代理合约中调用Maker资产合约的transferFrom()方法
  4. 如果Maker的ERC20资产合约调用失败,则交易回滚。
  5. 如果ERC20代理合约转换为交换合同。
  6. 资产交换合约将订单传递到ERC20Proxy合同。
  7. 在资产代理合约中调用Taker资产合约的transferFrom()方法
  8. 接受方ERC20代币合同失败后恢复。
  9. 从ERC20代理合约转换为资产交换合约
  10. 返回订单执行结果

3、部署0x智能合约

要与智能合约进行交互,我们需要部署0x智能合约,并使用智能合约的地址通过 0x.js库与智能合约进行交互。

首先安装0x.js:

1
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库进行交互:

1
2
3
4
5
6
7
8
9
10
let contractConfig = {
contractAddresses: {
erc20Proxy: proxyAddress.toLowerCase(),
erc721Proxy: "0x1d7022f5b17d2f8b695918fb48fa1089c9f85401",
exchange: exchangeAddress.toLowerCase()
},
networkId: networkId
};

const contractWrappers = new ContractWrappers(holderEngine, contractConfig);

现在就可以交互部署在专用或测试网络上的0x协议智能合约。 请记住添加RPC子提供程序以与区块链进行交互。

4、使用0x.js访问0x合约

为了与0x.js库进行交互,我们需要导入相关的软件包,如下所示,最终目标是使用0x.js库 用Maker帐户创建订单,并且Taker将使用fillOrder()方法提交执行交易:

1
2
3
4
5
6
7
8
9
10
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子提供程序:

1
2
3
4
const holderEngine = new Web3ProviderEngine();
holderEngine.addProvider(holderWallet);
holderEngine.addProvider(new RPCSubprovider(providerUrl));
holderEngine.start();

在RPC子提供程序中,可以使用自定义URL分别连接以太坊主网、测试网或私有区块链的区块链连接。

获取0x合约地址并实例化合约包装器:

1
2
3
const contractWrappers = new ContractWrappers(holderEngine, contractConfig);
const web3Wrapper = new Web3Wrapper(providerEngine);
const contractAddresses = getContractAddressesForNetworkOrThrow(100);//networkID

现在,Maker将创建订单,而Taker将执行订单以交换Maker的资产:

1
2
3
const tokenAAddress = contractAddresses.tokenA;
const tokenBAddress = contractAddresses.tokenB;
const exchange = contractAddresses.exchange;

所有地址都可以从0x.js库获取到。

1
2
3
4
5
6
7
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转让代币资产:

1
2
3
4
5
6
7
8
9
10
11
12
13
//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将在链上执行订单。

5、0x订单的创建、签名、验证与执行

创建订单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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对订单计算哈希:

1
const orderHashHex = orderHashUtils.getOrderHashHex(order);

获取订单的哈希后,Maker使用0x.js库的ecSignHashAsync()方法签名订单。

1
2
3
4
5
const signature = await signatureUtils.ecSignHashAsync(providerEngine, orderHashHex, maker);
const signedOrder = {
…order,
signature
};

Taker可以使用资产交易合约的validateFillOrder方法验证订单是否可以执行:

1
2
3
4
5
await contractWrappers.exchange.validateFillOrderThrowIfInvalidAsync(
signedOrder,
takerAssetAmount,
taker
);

Taker最终调用资产交易合约的fillOrder方法执行订单:

1
2
3
4
5
6
7
8
9
10
try{
txHash = await contractWrappers.exchange.fillOrderAsync(
signedOrder,
takerAssetAmount,
taker,
{TX_DEFAULTS,}
);

var transaction = await web3Wrapper.awaitTransactionSuccessAsync(txHash);
}catch(error){}

原文链接:How to integrate 0x(ZRX) Protocol to setup your own Decentralized Exchange (DEX)

汇智网翻译整理,转载请标明出处