[Ethereum] SolidityのContractをweb3.jsで呼び出したい

<body>
  <script src="https://cdn.jsdelivr.net/gh/ethereum/web3.js/dist/web3.min.js"></script>
	<script>
    const web3 = new Web3(new Web3.providers.HttpProvider("https://ropsten.infura.io/v3/*"));
      const address = "*";
      const abi = [{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"clients","outputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"gender","type":"string"},{"internalType":"uint256","name":"weight","type":"uint256"},{"internalType":"uint256","name":"height","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_gender","type":"string"},{"internalType":"uint256","name":"_weight","type":"uint256"},{"internalType":"uint256","name":"_height","type":"uint256"}],"name":"register","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"excesise","outputs":[{"internalType":"uint256","name":"newWeight","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}]

      const contract = new web3.eth.Contract(abi, address);
      const handleCall = async () => {
        await contract.methods.register("tanaka", "aaa", 65, 175);
        const result = await contract.methods.excesise.call();
        console.log(result);
      };
      handleCall();
      
    </script>
</body>
</html>

{arguments: Array(0), call: ƒ, send: ƒ, encodeABI: ƒ, estimateGas: ƒ, …}

何故だろう… 期待通りにいかん…

[ethereum] solidityをRopstenにデプロイしたい

$ npm install truffle-hdwallet-provider
$ truffle compile

truffleconfig.js

const HDWalletProvider = require("truffle-hdwallet-provider");
const mnemonic = "";

    ropsten: {
      provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/v3/${project_id}`),
      network_id: 3,       // Ropsten's id
      gas: 5500000,        // Ropsten has a lower block limit than mainnet
      confirmations: 2,    // # of confs to wait between deployments. (default: 0)
      timeoutBlocks: 200,  // # of blocks before a deployment times out  (minimum/default: 50)
      skipDryRun: true     // Skip dry run before migrations? (default: false for public nets )
    },

$ truffle migrate –network ropsten

なるほど、これは凄い

[ethereum] solidityで配列に格納して、値を返却する

Practice.sol
L 構造体Clientのclientsという配列に格納して、excesise関数でweightを-2として返却するコントラクトです。

pragma solidity >= 0.4.0 < 0.7.0;

contract Practice {

	struct Client {
		string name;
		string gender;
		uint256 weight;
		uint256 height;
	}
	Client[] public clients;

	function register(string memory _name, string memory _gender, uint256 _weight, uint256 _height) public returns(uint256 id) {
		id = clients.push(Client(_name, _gender, _weight, _height)) - 1;
		return id;
	}

	function excesise() public returns (uint256 newWeight){
		newWeight = clients[0].weight - 2;
		return newWeight;
	}

}

practice_test.js

	describe("practice()", () => {
		it("returns excesized weight", async() => {
			const practice = await PracticeContract.deployed();
			const expected = 63;
			await practice.register("yamada", "male", 65, 175);
			const actual = await practice.excesise.call();
			
			assert.equal(actual, expected, "Weight should be 63");
		})
	})

Contract: Practice
✓ has been deployed successfully
practice()
✓ returns weight (124ms)

2 passing (184ms)

[ethereum] solidityでtax計算

Practice.sol

contract Practice {

	function hello() external pure returns(string memory) {
		return "Hello, World!";
	}

	function tax(uint256 price) public pure returns(uint256 newPrice){
		 newPrice = price * 11 / 10;
		 return newPrice;
	}
}

practice_test.js

	describe("practice()", () => {
		it("returns tax in price", async() => {
			const practice = await PracticeContract.deployed();
			const expected = "110";
			const actual = await practice.tax(100);
			assert.equal(actual, expected, "should be 110");
		})
	})

$ truffle test

Compiling your contracts…
===========================
> Compiling ./contracts/Migrations.sol
> Compiling ./contracts/Practice.sol
> Artifacts written to /tmp/test-202209-108928-12xhvrv.xr0d
> Compiled successfully using:
– solc: 0.5.16+commit.9c3226ce.Emscripten.clang

Contract: Practice
✓ has been deployed successfully
practice()
✓ returns ‘Hello, World!’
practice()
✓ returns tax in price

3 passing (123ms)

taxのmultiplyは price * 1.1; とするとエラーになるので、price * 11 / 10;としないといけない。
なるほど、中々激しい。

[Ethereum] Solidityの基礎構文 その2

### contractでの宣言

pragma solidity ^0.4.19;

contract ZombieFactory {

    uint dnaDigits = 16;
}

uintは符号なし256ビットinteger(正のみ)、uint8、uint16、 uint32などもある

### 構造体

contract ZombieFactory {

    uint dnaDigits = 16;
    uint dnaModulus = 10 ** dnaDigits;
    
    struct Zombie {
        string name;
        uint dna;
    }

}

### 配列
solidityには固定長配列と可変長配列がある

uint[2] fixedArray;

string[5] stringArray;

uint[] dynamicArray;
```
publicな配列にすれば、他のコントラクトもこの配列を読める
```
contract ZombieFactory {

    uint dnaDigits = 16;
    uint dnaModulus = 10 ** dnaDigits;

    struct Zombie {
        string name;
        uint dna;
    }
    Zonbie[] public zombies;

}

### 関数

function eatHumburgers(string _name, uint _amount) {
	
}

eatHamburgers("vitalink", 100)

実例

    function createZombie(string _name, uint _dna){

    }

### 新しい構造体

Person satoshi = Person(20, "Satoshi");
people.push(satoshi)

people.push(Person(15, "akemi"));

関数の中に書く

    function createZombie(string _name, uint _dna) {
        // ここから始めるのだ
        zombies.push(Zombie(_name, _dna));
    }

### Private/Public
publicの関数は誰でもコントラクトの関数を呼び出して実行できる
以下の様に書くと、contract内の他の関数からのみ読み出せる

uint[] numbers;

function _addToArray(uint _number) private {
	numbers.push(_number);
}

privateの関数はアンダーバー(_)で始めるのが通例

    function _createZombie(string _name, uint _dna) private {
        zombies.push(Zombie(_name, _dna));
    }

### 関数の戻り値

string greeting = "what's up dog";

function sayHello() public returns (string) {
	return greeting;
}

関数で変更するにはviewを使う

function sayHello() public view returns (string) {
	return greeting;
}

テスト

    function _generateRandomDna(string _str) private view returns(uint){
        
    }

### Keccak256
ランダムな256ビットの16進数

    function _generateRandomDna(string _str) private view returns (uint) {
        uint rand = uint(keccak256(_str));
        return rand % dnaModulus;
    }

### 統合

    function createRandomZombie(string _name) public {
        uint randDna = _generateRandomDna(_name);
        _createZombie(_name, randDna);
    }

### event
ブロックチェーンで何かが生じた時にフロントエンドに伝えることができる
何かあったときにアクションを実行する

event IntegersAdded(uint x, uint y, uint result);

function add(uint _x, uint _y) public {
	uint result = _x + _y;
	IntegersAdded(_x, _y, result);
	return result;
}

js側

YourContract.IntegersAdded(function(error, result) {
  // 結果について何らかの処理をする
})

テスト

    event NewZombie(uint zombieId, string name, uint dna);

    function _createZombie(string _name, uint _dna) private {
        uint id = zombies.push(Zombie(_name, _dna)) - 1;
        NewZombie(id, _name, _dna);
    }

### フロントエンド

var abi = ""
var ZombieFactoryContract = web3.eth.contract(abi)
var ZombieFactory = ZombieFactoryContract.at(contractAddress)

#("ourButton").click(function(e) {
	var name = $("#nameInput").val()
	ZombieFactory.createRandomZombie(name)
})

var event = ZombieFactory.NewZombie(function(error, result){
	if(error) return
	generateZombie(result.zombieId, result.name, result.dna)
})

function generateZombie(id, name, dna){
	let dnaStr = String(dna)

	while(dnaStr.length < 16)
		dnaStr = "0" + dnaStr

	let zombieDetails = {
		headChoice: dnaStr.substring(0, 2) % 7 + 1,
	    eyeChoice: dnaStr.substring(2, 4) % 11 + 1,
	    shirtChoice: dnaStr.substring(4, 6) % 6 + 1,
	    skinColorChoice: parseInt(dnaStr.substring(6, 8) / 100 * 360),
	    eyeColorChoice: parseInt(dnaStr.substring(8, 10) / 100 * 360),
	    clothesColorChoice: parseInt(dnaStr.substring(10, 12) / 100 * 360),
	    zombieName: name,
	    zombieDescription: "A Level 1 CryptoZombie",
	}

	return zombieDetails
}

なるほど、ゾンビとかふざけ気味だが、中々のものだな

[Ethereum] Solidityの基礎構文 その1

$ truffle init
$ touch test/practice_test.js

const PracticeContract = artifacts.require("Practice");

contract("Practice", () => {
	it("has been deployed successfully", async() => {
		const practice = await PracticeContract.deployed();
		assert(practice, "contract was not deployed");
	})
})

artifacts.require(“*”) でコンパイル済みのコントラクトを読み込んで操作できる

$ touch contracts/Practice.sol

pragma solidity >= 0.4.0 < 0.7.0;

contract Practice {
	
}

migrationによってcontractがdeployされる
$ touch migrations/2_deploy_practice.js

const PracticeContract = artifacts.require("Practice");

module.exports = function(deployer) {
	deployer.deploy(PracticeContract);
}

contract

pragma solidity >= 0.4.0 < 0.7.0;

contract Practice {

	function hello() external pure returns(string memory) {
		return "Hello, World!";
	}
}

– hello : 関数
– external関数 : practice contactのインターフェイスの一部であり、他のコントラクトから呼び出せるが、コントラクトの中からは呼び出せない。external以外に、internal, public, privateがある。publicは他のコントラクトから呼び出せるし、内部でも呼び出せる。externalをpublicに変更しても結果は同じ。internalとprivateではオブジェクトやthisでは呼び出せない。
– pure, view: コントラクトの変数の状態を変更しない関数 pureはデータを読み込むことも書き込むこともできない viewは読み込むだけ
– memory : 永続ストレージに配置されているものを一切参照しない

なるほど 分解すると分かりやすい

[Ethereum] Ubuntuでmining

まず、Minergateに登録する
https://minergate.com/reg

$ sudo apt-get update
$ sudo apt-get install software-properties-common
$ sudo add-apt-repository -y ppa:ethereum/ethereum-qt
$ sudo add-apt-repository ppa:ethereum/ethereum
$ sudo add-apt-repository ppa:ethereum/ethereum-dev
$ sudo apt-get update
$ sudo apt-get install ethminer
$ sudo apt-get install ethminer
Reading package lists… Done
Building dependency tree
Reading state information… Done
E: Unable to locate package ethminer
これだと上手くいかない

$ sudo apt install nvidia-cuda-toolkit
$ nvcc -V
nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2019 NVIDIA Corporation
Built on Sun_Jul_28_19:07:16_PDT_2019
Cuda compilation tools, release 10.1, V10.1.243

$ mkdir ethminer
$ wget -O ethminer/ethminer.tar.gz https://github.com/ethereum-mining/ethminer/releases/download/v0.18.0/ethminer-0.18.0-cuda-9-linux-x86_64.tar.gz
$ tar xzf ethminer/ethminer.tar.gz -C ethminer/
$ ethminer/bin/ethminer –help
$ ethminer/bin/ethminer -U -P stratum+tcp://eth.pool.minergate.com:45791/*@gmail.com

なるほど 手順はわかった
あとはsolidityの基礎とreact, truffle周りかな

[Ethereum] ERC721トークン

ERC721は、スマートコントラクト内でNon-Fungible Tokenを扱えるようにしたもの

pragma solidity ^0.4.23;

import "github.com/OpenZeppelin/openzeppelin-solidity/contracts/token/ERC721/ERC721Full.sol";
import "github.com/OpenZeppelin/openzeppelin-solidity/contracts/token/ERC721/ERC721Mintable.sol";
import "github.com/OpenZeppelin/openzeppelin-solidity/contracts/ownership/Ownable.sol";

contract MyTokenCollection is ERC721Full, ERC721Mintable, Ownable {
	
	constructor() public ERC721Full("MyTokenCollection", "MTC"){}

	function mintTokenCollection(string _tokenURI) public {
		uint256 newTokenId = _getNextTokenId();
		_mint(msg.sender, newTokeId);
		_setTokenURI(newTokenId, _tokenURI);
	}

	function _getNextTokenId() private view returns(uint256){
		return totalSupply().add(1);
	}
}

なるほど、Truffleもう一回ちょっとやり直す必要があるかな
Reactの勉強もしないといけないか…

[Ethereum] Reactでdappを作りたい

$ sudo npm install -g create-react-app
$ create-react-app lottery-react
$ cd lottery-react
$ npm run start

http://192.168.34.10:3000/

$ sudo npm install -g yarn
$ yarn -v
1.22.5
$ npm install –save web3@1.0.0-beta.26

$ cd ..
$ mkdir lottery
$ cd lottery
$ sudo npm install solc@0.4.17
$ npm install –save mocha ganache-cli web3@1.0.0-beta.26
$ npm install –save truffle-hdwallet-provider@0.0.3

contract/Lottery.sol

pragma solidity ^0.4.17;

contract Lottery {
	address public manager;
	address[] public players;

	function Lottery () public {
		manager = msg.sender;
	}

	function enter() public payable {
		require(msg.value > .01 ether);

		players.push(msg.sender);
	}

	function random() private view return (uint) {
		return uint(keccak256(block.difficulty, now, players));
	}

	function pickWinner() public restricted {
		uint index = random() % players.length;
		players[index].transfer(this.balance);
		players = new address[](0);
	}

	modifier restricted() {
		require(msg.sender == manager);
		_;
	}

	function getPlayers() public view returns(address[]){
		return players;
	}
}

compiler.js

const path = require('path');
const fs = require('fs');
const solc = require('solc');

const IndexPath = path.resolve(__dirname,'contracts', 'Lottery.sol');
const source = fs.readFileSync(IndexPath, 'utf8');

console.log(solc.compile(source,1));

ABIとは、Application Binary Interface

lottery.js

import web3 from './web3';

const address = '0xaC24CD668b4A6d2935b09596F1558c1E305F62F1';


const abi = [{
    "constant": true,
    "inputs": [],
    "name": "manager",
    "outputs": [{
        "name": "",
        "type": "address"
    }],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
}, {
    "constant": false,
    "inputs": [],
    "name": "pickWinner",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
}, {
    "constant": true,
    "inputs": [],
    "name": "getPlayers",
    "outputs": [{
        "name": "",
        "type": "address[]"
    }],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
}, {
    "constant": false,
    "inputs": [],
    "name": "enter",
    "outputs": [],
    "payable": true,
    "stateMutability": "payable",
    "type": "function"
}, {
    "constant": true,
    "inputs": [{
        "name": "",
        "type": "uint256"
    }],
    "name": "players",
    "outputs": [{
        "name": "",
        "type": "address"
    }],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
}, {
    "inputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "constructor"
}];


export default new web3.eth.Contract(abi, address);

web3.js

import Web3 from 'web3';

const web3 = new Web3(window.web3.currentProvider);

export default web3;

App.js

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import web3 from './web3';
import lottery from './lottery';

class App extends Component {
  state = {
    manager: '',
    player: [],
    balance: '',
    value: '',
    messaget: ''
  };

  async componentDidMount() {
    const manager = await lottery.methods.manager().call();
    const players = await lottery.methods.getPlayers().call();
    const balance = await web3.eth.getBalance(lottery.opetions.address);

    this.setState({manager, players, balance});
  }

  onSubmit = async(event) => {
    event.preventDefault();

    const accounts = await web3.eth.getAccounts();

    this.setState({message: 'Waiting on transaction success...'});

    await lottery.methods.enter().send({
      from: accounts[0],
      value: web3.utils.toWei(this.state.value, 'ether')
    });

    this.setState({message: 'you have been entered!'});
  };

  onClick = async() => {
    const accounts = await web3.eth.getAccounts();

    this.setState({message: "waiting on transaction success..."});

    await lottery.methods.pickWinner().send({
      from: accounts[0]
    });

    this.setState({message: 'A winner has been picked'});
  };

  render() {
    return (
    <div>
      <h2>Lottery Contract</h2>
      <p>
        This contract is managed by {this.state.manager}.
        There are currently {this.state.players.length} peple entered,
        competing to win {web3.utils.fromWei(this.state.balance, 'ether')}
      </p>
      <hr />

        <form onSubmit={this.onSubmit}>
            <h4>Want to try your luck?</h4>
            <div>
              <label>Amount of ether to enter</label>
              <input
              value = {this.state.value}
                onChange={event => this.setState({ value: event.target.value })}
              />
            </div>
            <button>Enter</button>
        </form>

        <hr />

        <h4>Ready to pick a winner?</h4>
        <button onClick={this.onClick}>Pick a winner!</button>

        <hr />
        <h1>{this.state.message}</h1>
      </div>
  );
  }
}

export default App;

なるほど、全体の流れはわかった

[Ethereum] wallet address, mnemonicの作成

Ethereum uses ECDSA to generate public-private key pairs as Bitcoin does.
Ethereum address is generated by hashing public key with keccak256, slicing last 20 bytes, and adding “0x” at the top.

privateKey -> derive with ECDSA -> publicKey -> hash -> Ethereum address

const Web3 = require("web3");

const mnemonic = "*";

const HDWalletProvider = require('truffle-hdwallet-provider');
let provider = new HDWalletProvider(mnemonic, 'https://ropsten.infura.io/v3/*', 0, 1);

let web3 = new Web3(provider);

let account = web3.eth.accounts.create();

console.log("address:" + account.address);
console.log("privateKey:" + account.privateKey);

$ node address.js
address:0xefC5B5d9A4BDA3bE4f4da8e7AaFe25F08C5EAce7
privateKey:********
$ node address.js
address:0xB19C2356a88F5437e253d404DA642EC914dCC794
privateKey:********

ニーモニック(mnemonic)はあるだけで口座管理ができる
キーストアはパスワードがないと何も出来ない
MetaMaskのウォレットではmnemonicから秘密鍵を作る仕組みがある

### mnemonic
12ワードは128bitのエントロピーから生成される(randomな値)
エントロピーからチェックサムを生成し足し合わせバイト列に変換し11bit(最大2048)ごとに分割
分割した値をindex値としてワードリストの索引
PBKDF2-SHA512から生成 2048回ストレッチ

なるほど、中々凄いことになってるわ