TruffleでSmartContractの作成と管理

#はじめに
Browser-solidityなどコントラクトを書く手段はいくつかありますが、
コードの管理や、コンパイル、デプロイなど、都度異なる方法でやっていると雑多になってしまうので、TruffleというEthereum開発フレームワークを使って行きます。

#チュートリアル
truffleframeworkにはpet-shopを作るチュートリアルが公開されています。
http://truffleframework.com/tutorials/pet-shop#interacting-with-the-dapp-in-a-browser

#流れ

```
コントラクトの作成
$ truffle create contract コントラクト名
↓
コンパイル
$ truffle compile
Solidityで書かれたコードをEVM(EthereumVirtualMachine)で実行するためにバイトコードに変換する
↓
デプロイ
$ truffle migration
testnetなど環境を選択した上で、ブロックチェーンネットワークへdeployする
↓
テスト
$ truffle test
```

#MyToken(独自トークン)を作る事例

##1.Truffleをインストール

```
$ npm install -g truffle
//フォルダ作成
$ mkdir ~/battle-coin
$ cd ~/battle-coin
//初期化
$ truffle init
npm init -f
//OpenZeppelinライブラリを入れる
npm i zeppelin-solidity --save
```
##2.Tokenの作成
```
$ truffle create contract MyToken (<- contracts/MyToken.solが作られる)
$ vim contracts/MyToken.sol
```

```MyToken.sol
pragma solidity ^0.4.17;

import 'zeppelin-solidity/contracts/token/StandardToken.sol';

contract MyToken is StandardToken {
  string public constant name = "BattleCoin";
  string public constant symbol = "BAC";
  uint256 public constant decimals = 0;
  uint256 public constant INITIAL_SUPPLY = 100000000;

  function MyToken() {
    totalSupply = INITIAL_SUPPLY;
    balances[msg.sender] = INITIAL_SUPPLY;
  }
}
```

```
$ touch migrations/2_deploy_battle_coin.js
```

```migrations/2_deploy_battle_coin.js

const MyToken2 = artifacts.require("./MyToken.sol");

module.exports = function(deployer) {
  	//deployer.deploy(MyToken);
	let initialSupply = 1000000
    let maxSupply = 100000000
	deployer.deploy(MyToken, initialSupply, maxSupply, {
        gas: 2000000
    })
};

```

##3.deployする

```truffle.js
var HDWalletProvider = require("truffle-hdwallet-provider");
var mnemonic = "traction unplanned litigate game shrank preppy disorder gumdrop ..........";
var accessToken = "aaa";

module.exports = {
  networks: {
    ropsten: {
      provider: function() {
        return new HDWalletProvider(
          mnemonic,
          "https://ropsten.infura.io/" + accessToken
        );
      },
      network_id: 3,
      gas: 500000
    }   
  }
};
```

```
//truffle-hdwallet-providerを入れる
npm install truffle-hdwallet-provider

//接続する
truffle console --network ropsten

//アカウントのアドレスを確認する
truffle(ropsten)> web3.eth.getAccounts(function(err, res){console.log(res)})

//必要なら開発用のetherを入れておく
$ curl -X POST  -H "Content-Type: application/json" -d '{"toWhom":"0x76487f6311c70d08c052a113b5c26436ce837b8c"}' https://ropsten.faucet.b9lab.com/tap

//確認
https://ropsten.etherscan.io/address/0x76487f6311c70d08c052a113b5c26436ce837b8c

//migrateする
truffle(ropsten)>migrate

//確認する
truffle(develop)> myToken = MyToken.at(MyToken.address)
truffle(develop)> myToken.name()
truffle(develop)> myToken.totalSupply()

//送金する
myToken.transfer("0xC2b10c8727E8EDBf03Dd9EC69845D99bBDfAB11a", 23)
```

##4.独自tokenをMyEtherWalletに送金して管理する
<img width="553" alt="スクリーンショット 2018-01-15 14.47.06.png" src="https://qiita-image-store.s3.amazonaws.com/0/43766/0706c259-d024-fb3c-3a25-32e53dc6318d.png">

<img width="835" alt="スクリーンショット 2018-01-15 14.55.59.png" src="https://qiita-image-store.s3.amazonaws.com/0/43766/d2d0f711-fd63-9c5f-0bac-4991bd88c0fe.png">


##5.おまけ(Web3.jsを使って、作成したtokenをやり取りする)

###token残高のsample
```balance.js
#!/usr/bin/env node
var Web3 = require('web3');
var web3 = new Web3();
web3.setProvider(new web3.providers.HttpProvider('https://ropsten.infura.io/'));
web3.eth.defaultAccount=web3.eth.accounts[0]
//Contract details (bytecode, interface etc.)のInterfaceをabiとして貼り付ける
var abi = [{"constant": true,"inputs": [],"name": "name","outputs": [{"name": "","type": "string"}],"payable": false,"stateMutability": "view","type": "function"},{"constant": false,"inputs": [{"name": "_spender","type": "address"},{"name": "_value","type": "uint256"}],"name": "approve","outputs": [{"name": "","type": "bool"}],"payable": false,"stateMutability": "nonpayable","type": "function"},{"constant": true,"inputs": [],"name": "totalSupply","outputs": [{"name": "","type": "uint256"}],"payable": false,"stateMutability": "view","type": "function"},{"constant": false,"inputs": [{"name": "_from","type": "address"},{"name": "_to","type": "address"},{"name": "_value","type": "uint256"}],"name": "transferFrom","outputs": [{"name": "","type": "bool"}],"payable": false,"stateMutability": "nonpayable","type": "function"},{"constant": true,"inputs": [],"name": "INITIAL_SUPPLY","outputs": [{"name": "","type": "uint256"}],"payable": false,"stateMutability": "view","type": "function"},{"constant": true,"inputs": [],"name": "decimals","outputs": [{"name": "","type": "uint256"}],"payable": false,"stateMutability": "view","type": "function"},{"constant": false,"inputs": [{"name": "_spender","type": "address"},{"name": "_subtractedValue","type": "uint256"}],"name": "decreaseApproval","outputs": [{"name": "","type": "bool"}],"payable": false,"stateMutability": "nonpayable","type": "function"},{"constant": true,"inputs": [{"name": "_owner","type": "address"}],"name": "balanceOf","outputs": [{"name": "balance","type": "uint256"}],"payable": false,"stateMutability": "view","type": "function"},{"constant": true,"inputs": [],"name": "symbol","outputs": [{"name": "","type": "string"}],"payable": false,"stateMutability": "view","type": "function"},{"constant": false,"inputs": [{"name": "_to","type": "address"},{"name": "_value","type": "uint256"}],"name": "transfer","outputs": [{"name": "","type": "bool"}],"payable": false,"stateMutability": "nonpayable","type": "function"},{"constant": false,"inputs": [{"name": "_spender","type": "address"},{"name": "_addedValue","type": "uint256"}],"name": "increaseApproval","outputs": [{"name": "","type": "bool"}],"payable": false,"stateMutability": "nonpayable","type": "function"},{"constant": true,"inputs": [{"name": "_owner","type": "address"},{"name": "_spender","type": "address"}],"name": "allowance","outputs": [{"name": "","type": "uint256"}],"payable": false,"stateMutability": "view","type": "function"},{"inputs": [],"payable": false,"stateMutability": "nonpayable","type": "constructor"},{"anonymous": false,"inputs": [{"indexed": true,"name": "owner","type": "address"},{"indexed": true,"name": "spender","type": "address"},{"indexed": false,"name": "value","type": "uint256"}],"name": "Approval","type": "event"},{"anonymous": false,"inputs": [{"indexed": true,"name": "from","type": "address"},{"indexed": true,"name": "to","type": "address"},{"indexed": false,"name": "value","type": "uint256"}],"name": "Transfer","type": "event"}];

//var _balance = web3.eth.contract(abi).at("コントラクトのアドレス").balanceOf("残高確認用のアドレス");
var _balance = web3.eth.contract(abi).at("0x4B5361044c2AD394F4E6696A1315Ac5Bc221a095").balanceOf("0x76487f6311c70d08c052a113b5c26436ce837b8c");
console.log(_balance);
```
###token送付するsample

```send_token.js
getTokenContractInstance: function () {
    const tokenAddress = "---tokenのアドレス---";
    const contractABI = [---ABIの中身---]
    const tokenContract = web3.eth.contract(contractABI);
    return tokenContract.at(tokenAddress);
},

sendToken: async function (toAddress, amount, gasLimit) {
    const payload = getTokenContractInstance().transfer.getData(toAddress, amount);
    const newTx = await constructNewTx(tokenAddress, 0, gasLimit, payload, Chains.Ropsten);
    submitRawTxToBlockchain(newTx, (result, error) => {
        if ((error)) {
            console.log(error)
        } else {
            console.log(result)
        }
    });
},

constructNewTx: async function (toAddress, amount, gasLimit, data, chainId) {
    const transactionCount = await getTransactionCount(toAddress);
    const gasPrice = await getGasPrice();
    return new Promise((resolve, reject) => {
        web3.eth.getTransactionCount(walletAddress, (error, result) => {
            if (error) {
                reject(error)
            } else {
                const newTxParams = {
                    nonce: "0x" + result,
                    gasPrice: "0x" + gasPrice,
                    gasLimit: "0x" + Number(gasLimit).toString(16),
                    to: toAddress,
                    value: "0x" + Number(web3.toWei(amount, "ether")).toString(16),
                    data: data,
                    chainId: chainId
                };
                resolve(newTxParams)
            }
        })
    });
},

getTransactionCount: function (address) {
    return new Promise((resolve, reject) => {
        web3.eth.getTransactionCount(address, (error, result) => {
            if (error) {
                reject(error)
            } else {
                resolve(result.toString('16'))
            }

        })
    });
},

getGasPrice: function() {
  return new Promise((resolve, reject) => {
        web3.eth.getGasPrice((error, result) => {
            if (error) {
                reject(error)
            } else {
                resolve(result.toString('16'))
            }
        })
  })
},

```