0x.jsを使って0xプロトコルのスマートコントラクトを使用する（詳細編_Taker側の処理）

# モチベーション
オーダーをTakerが記入（Fill）するときのプロセスを明記する。`fill_order_erc20.ts`を読み解く。間違い、ご意見あれば[Twitter経由](https://twitter.com/WatanabeSota?lang=en)でご連絡ください。

# コントラクトの全体的な流れ
1. MakerがほしいZRXとそれと交換するWETHを指定
2. Maker, Takerが0x PeoxyにZRXとWETHを動かす許可を与える
3. TakerがWETHコントラクトにETHをデポジットし、`Maker ZRX Approval` `Taker WETH Approval` `Taker WETH Deposit`を得る
4. 有効期限とエクスチェンジコントラクトのアドレスを指定
5. Orderの作成
6. Fill Order発動

その他メモ：このコントラクトは0x.jsから複数のファイルをインポート。特に大事なのは`Order`と
`Web3Wrapper`。これは`@0x/web3-wrapper`からインポートしている。

このコントラクトは、ZRX→WETH用である。takerは0xのExchange Contractを通してfillする。

## 1. MakerがほしいZRXとそれと交換するWETHを指定

```typescript
export async function scenarioAsync(): Promise<void> {
     PrintUtils.printScenario('Fill Order');
     // Initialize the ContractWrappers, this provides helper functions around calling
     // 0x contracts as well as ERC20/ERC721 token contracts on the blockchain
     const contractWrappers = new ContractWrappers(providerEngine, { networkId: NETWORK_CONFIGS.networkId });
     // Initialize the Web3Wrapper, this provides helper functions around fetching
     // account information, balances, general contract logs
     const web3Wrapper = new Web3Wrapper(providerEngine);
     const [maker, taker] = await web3Wrapper.getAvailableAddressesAsync();
     const zrxTokenAddress = contractAddresses.zrxToken;
     const etherTokenAddress = contractAddresses.etherToken;
     const printUtils = new PrintUtils(
         web3Wrapper,
         contractWrappers,
         { maker, taker },
        
         { WETH: etherTokenAddress, ZRX: zrxTokenAddress },
     );
     printUtils.printAccounts();
```

constで定数を指定している。ここで指定された値は後使うことになる。`web3Wrapper`は分かりづらいが、`node_modules/@0x/web3-wrapper/`から来ている。

# 2. Maker, Takerが0x PeoxyにZRXとWETHを動かす許可を与える

```typescript
const makerZRXApprovalTxHash = await contractWrappers.erc20Token.setUnlimitedProxyAllowanceAsync(
     zrxTokenAddress,
     maker,
);
await printUtils.awaitTransactionMinedSpinnerAsync('Maker ZRX Approval', makerZRXApprovalTxHash);
```
0x ERC20 ProxyにmakerAccountを代表してZRXを移動することを許可するコントラクト。`makerZRXApprovalTxHash`などは0x.jsのドキュメントからその役割を確認することができる。

```typescript
const takerWETHApprovalTxHash = await contractWrappers.erc20Token.setUnlimitedProxyAllowanceAsync(
     etherTokenAddress,
     taker,
);
await printUtils.awaitTransactionMinedSpinnerAsync('Taker WETH Approval', takerWETHApprovalTxHash);
```
上記コントラクトと構文は一緒。0x ERC20 ProxyにtakerAccountを代表してWETHを移動することを許可するコントラクト。

ここからTakerのWETHと、MakerのZRXを交換するコントラクトが`fill_order_erc20.ts`であるとわかる。

# 3. TakerがWETHコントラクトにETHをデポジットし、ETHと同額のWETHを受け取ることで、Maker ZRX Approval Taker WETH Approval Taker WETH Depositを得る
```typescript
const takerWETHDepositTxHash = await contractWrappers.etherToken.depositAsync(
     etherTokenAddress,
     takerAssetAmount,
     taker,
);
await printUtils.awaitTransactionMinedSpinnerAsync('Taker WETH Deposit', takerWETHDepositTxHash);
 
PrintUtils.printData('Setup', [
     ['Maker ZRX Approval', makerZRXApprovalTxHash],
     ['Taker WETH Approval', takerWETHApprovalTxHash],
     ['Taker WETH Deposit', takerWETHDepositTxHash],
]);
```
TakerはETHをWETHに交換しなければZRXと交換できないので、ETHをWETHコントラクトにデポジットすることで同額のWETHを受け取る。

# 4. 取引期限と取引コントラクトのアドレスを指定
```typescript
const randomExpiration = getRandomFutureDateInSeconds();
const exchangeAddress = contractAddresses.exchange;
```
デモなのでランダムに有効期限を指定している。エクスチェンジコントラクトアドレスはネットワークにより異なるので、そのアドレスを指定している。

# 5. オーダーを作成する
```typescript
const order: Order = {
    exchangeAddress,
    makerAddress: maker,
    takerAddress: NULL_ADDRESS,
    senderAddress: NULL_ADDRESS,
    feeRecipientAddress: NULL_ADDRESS,
    expirationTimeSeconds: randomExpiration,
    salt: generatePseudoRandomSalt(),
    makerAssetAmount,
    takerAssetAmount,
    makerAssetData,
    takerAssetData,
    makerFee: ZERO,
    takerFee: ZERO,
};
 
printUtils.printOrder(order);
```
オーダーを作成する。パラメーターは予め決まっている。詳細は[こちら](https://0x.org/wiki#Create,-Validate,-Fill-Order)から確認できる。
特に大事なのは`feeRecipientAddress`。リレイヤーはここに自分のアドレスを入れることによって手数料を得ることができる。

# 6.Fillオーダーの発動
```typescript
await printUtils.fetchAndPrintContractAllowancesAsync();
await printUtils.fetchAndPrintContractBalancesAsync();
 
   
const orderHashHex = orderHashUtils.getOrderHashHex(order);
const signature = await signatureUtils.ecSignHashAsync(providerEngine, orderHashHex, maker);
const signedOrder = { ...order, signature };
 
await contractWrappers.exchange.validateFillOrderThrowIfInvalidAsync(signedOrder, takerAssetAmount, taker);
 
   
txHash = await contractWrappers.exchange.fillOrderAsync(signedOrder, takerAssetAmount, taker, {
     gasLimit: TX_DEFAULTS.gas,
});
txReceipt = await printUtils.awaitTransactionMinedSpinnerAsync('fillOrder', txHash);
printUtils.printTransaction('fillOrder', txReceipt, [['orderHash', orderHashHex]]);
 
await printUtils.fetchAndPrintContractBalancesAsync();
 

providerEngine.stop();
 }
 
void (async () => {
   try {
         if (!module.parent) {
             await scenarioAsync();
         }
   } catch (e) {
       console.log(e);
       providerEngine.stop();
       process.exit(1);
   }
})();
```
オーダーの残高の確認後、オーダーハッシュを作成し署名する。 fillOrderを呼び出す前にオーダーを検証しMaker, Takeｒの残高があるか、0xスマートコントラクトに価値を交換する許可（Allowance）があるかを確認する。この一連の処理結果はターミナルに反映される。詳細は[こちら](https://qiita.com/SotaWatanabe/items/d872d2d43d2d5631c5b0)から確認できる。



# コントラクト全体
```typescript:fill_order_erc20.ts
import {
     assetDataUtils,
     BigNumber,
     ContractWrappers,
     generatePseudoRandomSalt,
     Order,
     orderHashUtils,
     signatureUtils,
 } from '0x.js';
 import { Web3Wrapper } from '@0x/web3-wrapper';
 
 import { NETWORK_CONFIGS, TX_DEFAULTS } from '../configs';
 import { DECIMALS, NULL_ADDRESS, ZERO } from '../constants';
 import { contractAddresses } from '../contracts';
 import { PrintUtils } from '../print_utils';
 import { providerEngine } from '../provider_engine';
 import { getRandomFutureDateInSeconds } from '../utils';
 
 /**
  * In this scenario, the maker creates and signs an order for selling ZRX for WETH.
  * The taker takes this order and fills it via the 0x Exchange contract.
  */
 export async function scenarioAsync(): Promise<void> {
     PrintUtils.printScenario('Fill Order');
     // Initialize the ContractWrappers, this provides helper functions around calling
     // 0x contracts as well as ERC20/ERC721 token contracts on the blockchain
     const contractWrappers = new ContractWrappers(providerEngine, { networkId: NETWORK_CONFIGS.networkId });
     // Initialize the Web3Wrapper, this provides helper functions around fetching
     // account information, balances, general contract logs
     const web3Wrapper = new Web3Wrapper(providerEngine);
     const [maker, taker] = await web3Wrapper.getAvailableAddressesAsync();
     const zrxTokenAddress = contractAddresses.zrxToken;
     const etherTokenAddress = contractAddresses.etherToken;
     const printUtils = new PrintUtils(
         web3Wrapper,
         contractWrappers,
         { maker, taker },
         { WETH: etherTokenAddress, ZRX: zrxTokenAddress },
     );
     printUtils.printAccounts();
 
     // the amount the maker is selling of maker asset
     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);
     // 0x v2 uses hex encoded asset data strings to encode all the information needed to identify an asset
     const makerAssetData = assetDataUtils.encodeERC20AssetData(zrxTokenAddress);
     const takerAssetData = assetDataUtils.encodeERC20AssetData(etherTokenAddress);
     let txHash;
     let txReceipt;
 
     // Allow the 0x ERC20 Proxy to move ZRX on behalf of makerAccount
     const makerZRXApprovalTxHash = await contractWrappers.erc20Token.setUnlimitedProxyAllowanceAsync(
         zrxTokenAddress,
         maker,
     );
     await printUtils.awaitTransactionMinedSpinnerAsync('Maker ZRX Approval', makerZRXApprovalTxHash);
 
     // Allow the 0x ERC20 Proxy to move WETH on behalf of takerAccount
     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
     const takerWETHDepositTxHash = await contractWrappers.etherToken.depositAsync(
         etherTokenAddress,
         takerAssetAmount,
         taker,
     );
     await printUtils.awaitTransactionMinedSpinnerAsync('Taker WETH Deposit', takerWETHDepositTxHash);
 
     PrintUtils.printData('Setup', [
         ['Maker ZRX Approval', makerZRXApprovalTxHash],
         ['Taker WETH Approval', takerWETHApprovalTxHash],
         ['Taker WETH Deposit', takerWETHDepositTxHash],
     ]);
 
     // Set up the Order and fill it
     const randomExpiration = getRandomFutureDateInSeconds();
     const exchangeAddress = contractAddresses.exchange;
 
     // Create the order
     const order: Order = {
         exchangeAddress,
         makerAddress: maker,
         takerAddress: NULL_ADDRESS,
         senderAddress: NULL_ADDRESS,
         feeRecipientAddress: NULL_ADDRESS,
         expirationTimeSeconds: randomExpiration,
         salt: generatePseudoRandomSalt(),
         makerAssetAmount,
         takerAssetAmount,
         makerAssetData,
         takerAssetData,
         makerFee: ZERO,
         takerFee: ZERO,
     };
 
     printUtils.printOrder(order);
 
     // Print out the Balances and Allowances
     await printUtils.fetchAndPrintContractAllowancesAsync();
     await printUtils.fetchAndPrintContractBalancesAsync();
 
     // Generate the order hash and sign it
     const orderHashHex = orderHashUtils.getOrderHashHex(order);
     const signature = await signatureUtils.ecSignHashAsync(providerEngine, orderHashHex, maker);
     const signedOrder = { ...order, signature };
 
     // Validate the order is Fillable before calling fillOrder
     // This checks both the maker and taker balances and allowances to ensure it is fillable
     // up to takerAssetAmount
     await contractWrappers.exchange.validateFillOrderThrowIfInvalidAsync(signedOrder, takerAssetAmount, taker);
 
     // 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]]);
 
     // Print the Balances
     await printUtils.fetchAndPrintContractBalancesAsync();
 
     // Stop the Provider Engine
     providerEngine.stop();
 }
 
 void (async () => {
     try {
         if (!module.parent) {
             await scenarioAsync();
         }
     } catch (e) {
         console.log(e);
         providerEngine.stop();
         process.exit(1);
     }
 })();
```