1、下载ETH钱包 下载 Ethereum Wallet或Mist其一即可,二者功能相同。
2、安装geth Mac下使用:
其余平台参考安装方法
3、编辑创世区块配置文件 genesis.json
:(参考示例 )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ludis@MacBook ~ cd Desktop/ethereum ludis@MacBook ~/Desktop/ethereum cat > genesis.json { "config": { "chainId": 33, "homesteadBlock": 0, "eip155Block": 0, "eip158Block": 0 }, "nonce": "0x0000000000000033", "timestamp": "0x0", "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "gasLimit": "0x8000000", "difficulty": "0x100", "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000", "coinbase": "0x3333333333333333333333333333333333333333", "alloc": {} } ^c
4、初始化 初始化区块链,并且创建一个文件夹来存储区块数据。由于第一步下载的钱包,其默认的区块数据存储在~/Library/Ethereum
目录,所以初始化时,将存储区块数据的文件夹指向该目录,这样钱包就和我们创建的私链绑定起来:
1 geth init genesis.json --datadir ~/Library/Ethereum
启动私链
1 geth --networkid 10 --rpc --rpcapi "admin,debug,eth,miner,net,personal,shh,txpool,web3" rpcaddr "0.0.0.0" --rpccorsdomain "*" --nodiscover --dev console
终端另开一个窗口,连接本地私链节点,开始挖矿。miner.start(1)
传的参数为挖矿所用的线程。
1 2 geth attach 'http://127.0.0.1:8545' miner.start(1)
如果提示挖矿账户错误,则需要设置挖矿账户为自己的钱包地址:
1 miner.setEtherbase("7df9a875a174b3bc565e6424a0050ebc1b2d1d82")
5、启动钱包 当私链启动后,打开钱包,这是会显示连接到私链成功。点击LAUNCH APPLICATION
进入钱包后,创建一个账户即可。
如果打开钱包,显示在同步节点数据,则说明本地私链启动失败,或与钱包绑定失败。
6、创建ERC20 token 创建一个基于ERC20规范的token,合约代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 pragma solidity ^0.4.19; // ---------------------------------------------------------------------------------------------- // Sample fixed supply token contract // Enjoy. (c) BokkyPooBah 2017. The MIT Licence. // ---------------------------------------------------------------------------------------------- // ERC Token Standard #20 Interface // https://github.com/ethereum/EIPs/issues/20 contract ERC20Interface { // 获取总的支持量 function totalSupply() constant returns (uint256 totalSupply); // 获取其他地址的余额 function balanceOf(address _owner) constant returns (uint256 balance); // 向其他地址发送token function transfer(address _to, uint256 _value) returns (bool success); // 从一个地址想另一个地址发送余额 function transferFrom(address _from, address _to, uint256 _value) returns (bool success); //允许_spender从你的账户转出_value的余额,调用多次会覆盖可用量。某些DEX功能需要此功能 function approve(address _spender, uint256 _value) returns (bool success); // 返回_spender仍然允许从_owner退出的余额数量 function allowance(address _owner, address _spender) constant returns (uint256 remaining); // token转移完成后出发 event Transfer(address indexed _from, address indexed _to, uint256 _value); // approve(address _spender, uint256 _value)调用后触发 event Approval(address indexed _owner, address indexed _spender, uint256 _value); } //继承接口后的实例 contract FixedSupplyToken is ERC20Interface { string public constant symbol = "XC"; //单位 string public constant name = "X Coin"; //名称 uint8 public constant decimals = 18; //小数点后的位数 uint256 _totalSupply = 100000000000000000000000000000; //发行总量 // 智能合约的所有者 address public owner; // 每个账户的余额 mapping(address => uint256) balances; // 帐户的所有者批准将金额转入另一个帐户。从上面的说明我们可以得知allowed[被转移的账户][转移钱的账户] mapping(address => mapping (address => uint256)) allowed; // 只能通过智能合约的所有者才能调用的方法 modifier onlyOwner() { assert(msg.sender == owner); _; } // 构造函数 function FixedSupplyToken(string _symbol,string _name) public { owner = msg.sender; balances[owner] = _totalSupply; //symbol = _symbol; //name = _name; } function totalSupply() constant public returns (uint256 totalSupply) { totalSupply = _totalSupply; } // 特定账户的代币余额 function balanceOf(address _owner) constant public returns (uint256 balance) { return balances[_owner]; } // 转移余额到其他账户 function transfer(address _to, uint256 _amount) public returns (bool success) { if (balances[msg.sender] >= _amount && _amount > 0 && balances[_to] + _amount > balances[_to]) { balances[msg.sender] -= _amount; balances[_to] += _amount; // balances[交易所地址] += 1; Transfer(msg.sender, _to, _amount); return true; } else { return false; } } //从一个账户转移到另一个账户,前提是需要有允许转移的余额 function transferFrom( address _from, address _to, uint256 _amount ) public returns (bool success) { if (balances[_from] >= _amount && allowed[_from][msg.sender] >= _amount && _amount > 0 && balances[_to] + _amount > balances[_to]) { balances[_from] -= _amount; allowed[_from][msg.sender] -= _amount; balances[_to] += _amount; Transfer(_from, _to, _amount); return true; } else { return false; } } //允许账户从当前用户转移余额到那个账户,多次调用会覆盖 function approve(address _spender, uint256 _amount) public returns (bool success) { allowed[msg.sender][_spender] = _amount; Approval(msg.sender, _spender, _amount); return true; } //返回被允许转移的余额数量 function allowance(address _owner, address _spender) constant public returns (uint256 remaining) { return allowed[_owner][_spender]; } }
7、创建项目 1. 创建一个node项目,来实现合约的编译、部署功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 ludis@MacBook ~ cd Desktop/contract ludis@MacBook ~ /Desktop/contract npm init This utility will walk you through creating a package.json file. It only covers the most common items, and tries to guess sensible defaults. See `npm help json` for definitive documentation on these fields and exactly what they do. Use `npm install <pkg>` afterwards to install a package and save it as a dependency in the package.json file. Press ^C at any time to quit. package name: (contract) version: (1.0.0) description: entry point: (index.js) test command: git repository: keywords: author: license: (ISC) About to write to /Users/ludis/Desktop/contract/package.json: { "name": "contract", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC" } Is this ok? (yes)
一路回车后初始化了一个node项目,接着安装所需的依赖:
1 ludis@MacBook ~/Desktop/contract cnpm i -S ganache-cli solc web3
创建以下目录结构:
1 2 3 4 5 6 7 . ├── build ├── compile.js ├── contracts ├── deploy.js ├── node_modules └── package.json
将第六步的合约命名为FixedSupplyToken.sol
保存在contracts
文件夹
2. compile.js
为编译合约用的脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 //compile.js 赋值编译智能合约 //require('库名/路径') const solc = require('solc'); //fs: file system const fs = require('fs'); const path = require('path'); //1.读取智能合约 //readFileSync 同步读取文件 //__dirname =>返回当前路径 console.log(__dirname); //resolve路径拼接的方法 const filepath = path.resolve(__dirname,'contracts','FixedSupplyToken.sol'); const contractFile = fs.readFileSync(filepath,'utf8'); // console.log(contractFile); //通过solc库来编译智能合约 var output = solc.compile(contractFile,1); // console.log(output); //output = {contracts:'',errors:'',sourceLiser:''} const contracts = output.contracts; // console.log(contracts); //for in 操作: contract = 对象 中的 属性名 for (let contract in contracts) { // console.log(contract) //contracts[contract] //写文件 fs // console.log(contract); let newfilepath = path.resolve(__dirname, 'build', contract.replace(':','') + '.json'); // console.log(newfilepath); //fs.writeFileSync(目标路径, 写入内容) //对象, tostring()方法, 返回是一个[object object] 字符串 //JSON.stringify => 将对象装换为字符串 fs.writeFileSync(newfilepath, JSON.stringify(contracts[contract])); } //console.log(contracts);
3. deploy.js
为部署合约用的脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 const ganache = require('ganache-cli'); const Web3 = require('web3'); const fs = require('fs'); const path = require('path'); // web3 小写是实例, Web3 是类 // web3 的构造函数需要传入一个rpc 地址 => provider(http, websocket) // 内存虚拟节点 // const web3 = new Web3(ganache.provider()); // 直接通过 http://127.0.0.1:8545链接到自己的节点 const web3 = new Web3('http://127.0.0.1:8545'); // 获取编译后的合约代码 const contractpath = path.resolve(__dirname,'build/FixedSupplyToken.json') // 解析成为json对象 const contract = JSON.parse(fs.readFileSync(contractpath,'utf8')); // 构建一个智能合约对象 let myContract = new web3.eth.Contract(JSON.parse(contract.interface)); web3.eth.getAccounts() .then(accounts => { // web3.personal.unlockAccount(eth.accounts[0],"asdqwe123", 15000) myContract.deploy({ data: '0x' + contract.bytecode,// bytecode arguments: ["MT", 'My TOKEN'] // 合约构造函数参数s }) .send({ from:accounts[0], gas: 1500000, // gas 消耗最大值 gasPrice: web3.utils.toWei("0.0000002", "ether") }) .then(d => { // 部署合约的返回结果是实例对象 d.methods.name().call().then(r => { console.log(r); }); // 合约方法调用 // 合约实例.methods.方法名(方法参数).call() d.methods.totalSupply().call().then(r => { console.log(r); }) // console.log(d) }) })
8、编译部署 编译: node compile.js
,编译成功会在build
文件夹生成编译完的json文件
部署: node deploy.js
,部署时会提示钱包账户解锁问题,只需终端执行web3.personal.unlockAccount(eth.accounts[0],"password", 15000)
,然后再部署即可。
部署成功后终端挖矿页面会显示区块的打包信息,当合约部署成功时会显示合约地址。然后把合约地址添加到钱包的contracts->token
中,就会显示该合约下的token数量。可以新建多个用户转账测试。