打开终端,启动testrpc
testrpc
EthereumJS TestRPC v6.0.3 (ganache-core: 2.0.2)
...
代币合约扮演的角色相当于银行的角色。使用者在代币合约中,用自己的以太币帐户地址当作银行帐户,可以透过代币合约执行转账(transfer
,将代币由一个帐户转到另一个帐户),查询余额(balanceOf
,查询指定帐户中拥有的代币)等原本由银行负责的工作。因为合约部署在公开区块链上,所有的交易都是公开透明,可供检验的。
cd /Users/mac/Desktop/GitHub/Solidity/learn/SmartContractDemo/EncryptedToken
macdeiMac:EncryptedToken mac$ pwd /Users/mac/Desktop/GitHub/Solidity/learn/SmartContractDemo/EncryptedToken
/Users/mac/Desktop/GitHub/Solidity/learn/SmartContractDemo/EncryptedToken
macdeiMac:EncryptedToken mac$ truffle init
Downloading...
Unpacking...
Setting up...
Unbox successful. Sweet!
Commands:
Compile: truffle compile
Migrate: truffle migrate
Test contracts: truffle test
macdeiMac:EncryptedToken mac$
终端执行truffle create contract EncryptedToken
命令创建EncryptedToken.sol
合约。
macdeiMac:EncryptedToken mac$ truffle create contract EncryptedToken
macdeiMac:EncryptedToken mac$
将下面的合约代码拷贝,替换EncryptedToken.sol
文件的代码。
pragma solidity ^0.4.22;//代表solidity的版本
//contract相当于其他语言中的class,EncryptedToken相当于类的名字。
//contract EncryptedToken可以理解为class EncryptedToken extends Contract。
contract EncryptedToken {
//声明了一个变量INITIAL_SUPPLY,初始化存储了一个666666的整数作为部署当前合约的钱包地址的代币数。
uint256 INITIAL_SUPPLY = 666666;
//balances是一个key类型为address,value类型为uint256的键值对(mapping),相当于Java中的map、iOS中的NSDictionary。
mapping(address => uint256) balances;
//EncryptedToken合约的构造函数(contructor),当EncryptedToken合约调用时,会先执行它的构造函数
constructor() public{
//以当前部署合约的钱包地址为key,以INITIAL_SUPPLY为value初始化一个键值对。
balances[msg.sender] = INITIAL_SUPPLY;
}
// 转账到一个指定的地址
/*
param:address _to 转账的目的地地址
param:uint256 _amount 转账金额
*/
function transfer(address _to, uint256 _amount) public{
//声明断言,当条件满足,即当前钱包余额小于要转账的额度时,就会抛出异常。
assert(balances[msg.sender] < _amount);
//从当前钱包额度中减去_amount
balances[msg.sender] -= _amount;
//将目标地址的额度增加_amount
balances[_to] += _amount;
}
// 查看指定地址的余额
/*
param:address _owner 指定的钱包地址
constant 关键字的作用 调用balanceOf函数时,它会自动调用call()方法
表明只是读书数据,而不需要往区块链写入数据,调用这个方法,不需要花费手续费
*/
function balanceOf(address _owner) public constant returns (uint256) {
return balances[_owner];
}
}
在migrations/
目录下创建一个名字叫做3_deploy_contract.js
的文件。文件中的内容为:
var EncryptedToken = artifacts.require('./EncryptedToken.sol');
module.exports = function(deployer) {
deployer.deploy(EncryptedToken);
}
修改truffle.js文件如下
module.exports = {
networks:{
development:{
host:"localhost",
port:8545,
network_id:"*"//匹配任何netword id
}
}
};
接下来执行compile和migrate命令:
macdeiMac:EncryptedToken mac$ truffle compile
Compiling ./contracts/EncryptedToken.sol...
Compiling ./contracts/Migrations.sol...
Writing artifacts to ./build/contracts
macdeiMac:EncryptedToken mac$ truffle migrate
Using network 'development'.
Running migration: 1_initial_migration.js
Deploying Migrations...
... 0xb294a83d6af78566da598d005e35a0c1aff1c9b9aab6bad47c2a7c64c8f78548
Migrations: 0xe32cf4c736d7eaed2e014f7922cc90e65291461a
Saving successful migration to network...
... 0xa4b7ffff3ae198a23b56aa79447811abc8073a877ce83a36c25638b10369cab9
Saving artifacts...
Running migration: 3_deploy_contract.js
Deploying EncryptedToken...
... 0x472380c714b1331d6b62202f489009e8aeb34a82f5f60fdfa04fed191c1b42cd
EncryptedToken: 0x87ef1502b36ec52e661091178b991211417815eb
Saving successful migration to network...
... 0x14cb8d7b4234d004b6d6e33a6524a4428e1d8f7202deb86556e2f37e6b21381d
Saving artifacts...
macdeiMac:EncryptedToken mac$
如上所示,我们已经将EncryptedToken
代币合约部署到了testrpc
上。
合约部署完成后,我们通过truffle console
开启console
控制台,在这个控制台中对已经部署的合约进行验证。
macdeiMac:EncryptedToken mac$ truffle console
truffle(development)> web3.eth.coinbase
'0x62c806c5751e952352ed23c21ea63b92f4d37e49'
truffle(development)> web3.eth.accounts[0]
'0x62c806c5751e952352ed23c21ea63b92f4d37e49'
truffle(development)> web3.eth.accounts[1]
'0x2148c956ae0d20110abfc741f47cab1812cd7100'
truffle(development)> web3.eth.accounts[2]
'0x3bd3500477294891fae9ec0d07b96864f648668c'
truffle(development)> web3.eth.accounts[3]
'0x40dcf037cc9bd7008eb268a93002b8ee760467cb'
truffle(development)> web3.eth.accounts[9]
'0x5dce4583cdda41ea1720ea54defc0febf779aa18'
truffle(development)>
在testrpc
启动时,系统会给我们分配10个钱包地址,如上图所示我们可以通过web3.eth.coinbase
或者web3.eth.accounts[0]
获取首个钱包地址,当然也可以根据索引获取其他的钱包地址。
接下来声明一个合约变量存储EncryptedToken
合约实例。
let contract;
undefined
truffle(development)> EncryptedToken.deployed().then(instance => contract = instance)
....
验证web3.eth.coinbase
和web3.eth.accounts[1]
中的余额。
truffle(development)> contract.balanceOf(web3.eth.coinbase)
BigNumber { s: 1, e: 5, c: [ 666666 ] }
truffle(development)> contract.balanceOf(web3.eth.accounts[1])
BigNumber { s: 1, e: 0, c: [ 0 ] }
truffle(development)>
经验证,第0
个钱包地址中的代币余额为666666
,第1个钱包地址中的代币余额为0
。
下一步,我们将从第0个账号中向第1个账号转账666个代币。
truffle(development)> contract.transfer(web3.eth.accounts[1], 666)
Error: VM Exception while processing transaction: invalid opcode
at XMLHttpRequest._onHttpResponseEnd (/usr/local/lib/node_modules/truffle/build/webpack:/~/xhr2/lib/xhr2.js:509:1)
at XMLHttpRequest._setReadyState (/usr/local/lib/node_modules/truffle/build/webpack:/~/xhr2/lib/xhr2.js:354:1)
at XMLHttpRequestEventTarget.dispatchEvent (/usr/local/lib/node_modules/truffle/build/webpack:/~/xhr2/lib/xhr2.js:64:1)
at XMLHttpRequest.request.onreadystatechange (/usr/local/lib/node_modules/truffle/build/webpack:/~/web3/lib/web3/httpprovider.js:128:1)
at /usr/local/lib/node_modules/truffle/build/webpack:/packages/truffle-provider/wrapper.js:134:1
at /usr/local/lib/node_modules/truffle/build/webpack:/~/web3/lib/web3/requestmanager.js:86:1
at Object.InvalidResponse (/usr/local/lib/node_modules/truffle/build/webpack:/~/web3/lib/web3/errors.js:38:1)
truffle(development)>
如上所示,转账过程中出现了异常,转账失败,仔细检查一下,不难发现是因为在我们合约代码EncryptedToken.sol
中有这么一句代码assert(balances[msg.sender] < _amount);
,也就是说只有当balances[msg.sender]
小于_amount
时,才不会出现异常,所以我们应该将<符号换成>符号,即当balances[msg.sender]
余额不足时抛出异常。
代码修改后,需要重新编译,部署。
truffle(development)> .exit
macdeiMac:EncryptedToken mac$ pwd /Users/mac/Desktop/GitHub/Solidity/learn/SmartContractDemo/EncryptedToken
/Users/mac/Desktop/GitHub/Solidity/learn/SmartContractDemo/EncryptedToken
macdeiMac:EncryptedToken mac$ ls
build migrations truffle-config.js
contracts test truffle.js
macdeiMac:EncryptedToken mac$ rm -rf build/
macdeiMac:EncryptedToken mac$ ls
contracts test truffle.js
migrations truffle-config.js
macdeiMac:EncryptedToken mac$ truffle compile
Compiling ./contracts/EncryptedToken.sol...
Compiling ./contracts/Migrations.sol...
Writing artifacts to ./build/contracts
macdeiMac:EncryptedToken mac$ truffle compile
macdeiMac:EncryptedToken mac$ truffle migrate --reset
Using network 'development'.
Running migration: 1_initial_migration.js
Deploying Migrations...
... 0x06dc2459d1475eb3695046aecf69853a5d48118b083e7b35029e28608fc9a31e
Migrations: 0x21784acbb71ea4ff918dcddfa6480ccd8dbd37f3
Saving successful migration to network...
... 0x1ee0008c47caa05a4e3c46fe74a1cf861db6cef360f89c42520dad0a7353447c
Saving artifacts...
Running migration: 3_deploy_contract.js
Deploying EncryptedToken...
... 0x0b4755c4c54c3aaa35458d0f2032146e112a884f6c1564258e8a6cc4a7b858d6
EncryptedToken: 0x428519366dbbf4f40f724a4270e60351e7550191
Saving successful migration to network...
... 0x161d89d9fe24bbd1bffc64585115519b5ed76c69a66929e63908b5fa086ef03e
Saving artifacts...
macdeiMac:EncryptedToken mac$
备注:编译时,一定要先将build
文件夹删除,其次在部署合约时,一定要添加--reset
,否则修改后的合约没法部署成功。
打开控制台,按照如下操作进行验证。
macdeiMac:EncryptedToken mac$ truffle console
truffle(development)> let contract
undefined
truffle(development)> EncryptedToken.deployed().then(instance => contract = instance)
...
truffle(development)> contract.balanceOf(web3.eth.coinbase)
BigNumber { s: 1, e: 5, c: [ 666666 ] }
truffle(development)> contract.balanceOf(web3.eth.accounts[1])
BigNumber { s: 1, e: 0, c: [ 0 ] }
truffle(development)> contract.transfer(web3.eth.accounts[1], 666)
{ tx: '0x3e864aa0b4509a6650dbd44115776e22e2ab3fb08620734ceef91a97d1cd2a43',
receipt:
{ transactionHash: '0x3e864aa0b4509a6650dbd44115776e22e2ab3fb08620734ceef91a97d1cd2a43',
transactionIndex: 0,
blockHash: '0x35feeaf4772df9abca434e2f9c5a60dcbb6d5bf9923f7d8dde6f109d768218ba',
blockNumber: 10,
gasUsed: 49083,
cumulativeGasUsed: 49083,
contractAddress: null,
logs: [],
status: 1 },
logs: [] }
truffle(development)> contract.balanceOf(web3.eth.coinbase)
BigNumber { s: 1, e: 5, c: [ 666000 ] }
truffle(development)> contract.balanceOf(web3.eth.accounts[1])
BigNumber { s: 1, e: 2, c: [ 666 ] }
truffle(development)>
如上所示,代币转账成功。
简单介绍了代币系统的逻辑,并没有对安全
进行相关操作,比如:余额不够的处理、地址合不合法的处理等等。