LoginSignup
11
9

More than 5 years have passed since last update.

TruffleでSmartContractの作成と管理

Last updated at Posted at 2018-01-18

はじめに

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に送金して管理する

スクリーンショット 2018-01-15 14.47.06.png

スクリーンショット 2018-01-15 14.55.59.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'))
            }
        })
  })
},

11
9
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
9