コントラクトをweb3.eth.sendTransactionでデプロイする方法

## まえがき

コントラクトをデプロイする際にcontract addressを取得せず、transaction hashだけすぐに欲しい場合に困ったのでメモ

詳しくは[こちら](https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI)を参照

## コントラクト

いくつかの引数をコンストラクタに持ったコントラクトを作成

``` solidity
pragma solidity ^0.4.23;

contract Sunday {

    uint        public uint_;
    string      public string_;
    bool        public bool_;
    address     public address_;
    uint[]      public uintArr_;
    
    constructor(uint _uint,string _string,bool _bool,address _address,uint[] _uintArr) public {
        uint_ = _uint;
        string_ = _string;
        bool_ = _bool;
        address_ = _address;
        uintArr_ = _uintArr;
    }

}
```

## コントラクトのコンパイル

``` javascript
// 必要なパッケージをインポート
const solc = require('solc');
const Web3 = require('web3');

// web3の初期化
const web3 = new Web3();
// ネットワークと接続
web3.setProvider(new web3.providers.HttpProvider('http://localhost:8545'));
// コントラクトの読込
let source = //　上記のコントラクトコード
// コンパイル
let contract = solc.compile(source, 1);
```

## 引数の値について

|  type   |                   value                    |                                                                                                                                                                       example                                                                                                                                                                        |
| ------- | ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| uint    | 123                                        | 000000000000000000000000000000000000000000000000000000000000007b                                                                                                                                                                                                                                                                                     |
| string  | test                                       | 00000000000000000000000000000000000000000000000000000000000000a0(offset) & 0000000000000000000000000000000000000000000000000000000000000003 + 6162630000000000000000000000000000000000000000000000000000000000                                                                                                                                       |
| boolean | true                                       | 0000000000000000000000000000000000000000000000000000000000000001                                                                                                                                                                                                                                                                                     |
| address | 0xca35b7d915458ef540ade6068dfe2f44e8fa733c | 000000000000000000000000ca35b7d915458ef540ade6068dfe2f44e8fa733c                                                                                                                                                                                                                                                                                     |
| uint[]  | [123,234,345]                              | 00000000000000000000000000000000000000000000000000000000000000e0(offset) & 0000000000000000000000000000000000000000000000000000000000000003 + 000000000000000000000000000000000000000000000000000000000000007b + 00000000000000000000000000000000000000000000000000000000000000ea + 0000000000000000000000000000000000000000000000000000000000000159 |

## デプロイコード

``` javascript
//　コントラクトコンパイルの続き

// 引数一覧
let uint = 123;
let string = 'abc';
let bool = true;
let address = '0xca35b7d915458ef540ade6068dfe2f44e8fa733c';
let uintArr = [123,234,345];

//　第1引数のuint
let uintHex = web3.toHex(uint)                                          // 値をHexへ変換
let uintValue = uintHex.slice(2)                                        // 先頭の0xを削除
let uintResult =  '0'.repeat(64 - uintValue.length) + uintValue         // 左側を0で埋めて合計64文字(32byte)にする

// 第2引数のoffsetを取得
let offset1 = web3.toHex(5 * 32)                                        // offsetの計算方法は省略
let offset1Value = offset1.slice(2)                                     // 先頭の0xを削除
let offset1Result = '0'.repeat(64 - offset1Value.length) + offset1Value // 左側を0で埋めて合計64文字(32byte)にする

//　第3引数のboolean
let flg = bool ? 1 : 0
let booleanHex = web3.toHex(flg)                                        // 値をHexへ変換
let booleanValue = booleanHex.slice(2)                                  // 先頭の0xを削除
let booleanResult = '0'.repeat(64 - booleanValue.length) + booleanValue // 右側を0で埋めて合計64文字(32byte)にする

//　第4引数のaddress
let addressValue = address.slice(2)                                     // 先頭の0xを削除
let addressResult = '0'.repeat(64 - addressValue.length) + addressValue // 左側を0で埋めて合計64文字(32byte)にする

// 第5引数のoffsetを取得
let offset2 = web3.toHex((5 + 2) * 32)                                  // offsetの計算方法は省略
let offset2Value = offset2.slice(2)                                     // 先頭の0xを削除
let offset2Result = '0'.repeat(64 - offset2Value.length) + offset2Value // 左側を0で埋めて合計64文字(32byte)にする

//　第2引数のstring
let stringHex = web3.toHex(string)                                      // 値をHexへ変換
let stringValue = intHex.slice(2)                                       // 先頭の0xを削除
let stringResult = stringValue + '0'.repeat(64 - stringValue.length)    // 右側を0で埋めて合計64文字(32byte)にする

//　第5引数のuint[]
let uintCount = uintArr.length
let uintArrValue = uintArr.map((item)=>{
    let itemHex = web3.toHex(item)                                      // 値をHexへ変換
    let itemValue = itemHex.slice(2)                                    // 先頭の0xを削除
    let itemResult = '0'.repeat(64 - itemValue.length) + itemValue      // 左側を0で埋めて合計64文字(32byte)にする
    return itemResult
})
let uintArrResult = '0'.repeat(64 - web3.toHex(uintCount).length) + web3.toHex(uintCount) + uintArrValue.join(''))
```

## デプロイ

``` javascript

let _data = contract.bytecode + 
            uintResult + 
            offset1Result + 
            booleanResult + 
            addressResult + 
            offset2Result + 
            stringResult + 
            uintArrResult

// 推奨ガス値取得
let _gas = web3.eth.estimateGas(_data)
// 平均ガスプライス取得
let _gasPrice = web3.eth.gasPrice
// トランザクション送出　戻り値はトランザクションハッシュ
let transaction = web3.eth.sendTransaction({
    from:_from,         // coinbase等トランザクション発行主を指定
    // to:_to,             // 今回は使用せず
    gas:_gas,           // 適当な値を指定
    gasPrice:_gasPrice, // 適当な値を指定
    data:_data,         // コントラクトのデプロイbytecode + コンストラクタの引数のHex値
    // value:_value,       // 今回は使用せず
    nonce:_nonce        // 適当な値を指定
})
```

## _dataの中身

見やすいように32bytes毎に区切ってます

``` javascript
0x...                                                            // ここにはコントラクトのbytecodeが入る。長すぎるので省略
000000000000000000000000000000000000000000000000000000000000007b // 第1引数 123
00000000000000000000000000000000000000000000000000000000000000a0 // 第2引数のoffset
0000000000000000000000000000000000000000000000000000000000000001 // 第3引数 true
000000000000000000000000ca35b7d915458ef540ade6068dfe2f44e8fa733c // 第4引数 0xca35b7d915458ef540ade6068dfe2f44e8fa733c
00000000000000000000000000000000000000000000000000000000000000e0 // 第5引数のoffset
0000000000000000000000000000000000000000000000000000000000000003 // 第2引数の文字数
6162630000000000000000000000000000000000000000000000000000000000 // 第2引数のHex値
0000000000000000000000000000000000000000000000000000000000000003 // 第5引数の配列の要素数
000000000000000000000000000000000000000000000000000000000000007b // 第5引数の配列[0]のHex値
00000000000000000000000000000000000000000000000000000000000000ea // 第5引数の配列[1]のHex値
0000000000000000000000000000000000000000000000000000000000000159 // 第5引数の配列[2]のHex値
```

## あとがき

駆け足でしたが以上。