接上一篇编写智能合约的教程带你玩转以太坊智能合约的”Hello World”,我们现在要建立一个简单的代币系统,具备最简单的转账和查询功能。
sily@lyg-sily:~$ testrpc
EthereumJS TestRPC v6.0.3 (ganache-core: 2.0.2)
Available Accounts
==================
(0) 0x73b5b6e153e03ec11eeeb3d623e50867a5e5227e
(1) 0xfdc2bb26d860ac1f84f3f06826ab1da4b3ef30b6
(2) 0x60e468066c781c1864479fb20cae2698eacc784b
(3) 0x2ea86e060b90cbe10cdba24e79496e6c46a483c7
(4) 0x51ada420bddc3a03c1294645784096bf663545b3
(5) 0xb8eaaf1449510e6d91229ddc6561d54a5dabef84
(6) 0xa1ab09f35f701ca1b91a8ae6ac800305a078e5ae
(7) 0xc84468b9c9f2a3d4e9c3a7861caa77a7f12593dd
(8) 0xd671b2eb8b0d0425b9dcc764bfee4fc8f19e6d5a
(9) 0x0351bb8bd2033ae15b8aee1ddf7d037fe12fd88c
Private Keys
==================
(0) eee65f7557d7c1271a2cf5848d816770bfcb18dab7c353858487299be4cf3f71
(1) 4391df2085e6c810c5b3125f37c81287e4697ed4076f2cfbe10f90cc23ac15e7
(2) 0ef1c08f8be81d79420ee3af55e657716ad79a9495c24e10a6ff8864c141c0fc
(3) da5766fd22e3b53b395d32ec9e544a15169f3ec54a1332c2146acf0002b37326
(4) fc4440a9fdc2e3bf9c6ab20107ef567433eaff1133d703c83fd26efb8b727ed3
(5) 88230785f8b2814659bc824b93e29bcf928e1af4371d53180fb276e8c8592402
(6) 3ce639e515d36811e45ccdd578732ec0799ba09156419d9c09a8b4a7029d70de
(7) 59ca1ade2f0a701a7d1e14ec5b886e9a835fea7d0aedf7226fa83cfe52c1e1fd
(8) c819bd2bdaeb8cc84f8b79ee0ff9089b99c725d6aca91ff793f2bc9c4df82243
(9) 376d808c1b708c126c45aa890ad4d208ac24cbe922fe5c97c952022e153423b4
HD Wallet
==================
Mnemonic: negative sea robot enforce tube cattle public engage jewel grit install fatigue
Base HD Path: m/44'/60'/0'/0/{account_index}
Listening on localhost:8545
前面我们搭建了智能合约的testrpc
测试环境,并通过truffle
框架进行了简单的HelloWorld
代码开发、编译、部署以及调用合约。接下来,我们要创建一个简单的代币,以继续熟悉truffle
框架和solidity
语法。
首先,回顾一下上次建立HelloWorld
项目的命令:
sily@lyg-sily:~$ mkdir SmartContractDemo
sily@lyg-sily:~$ cd SmartContractDemo/
sily@lyg-sily:~/SmartContractDemo$ mkdir HelloWorld
sily@lyg-sily:~/SmartContractDemo$ cd HelloWorld/
sily@lyg-sily:~/SmartContractDemo/HelloWorld$ truffle init
Downloading...
Unpacking...
Setting up...
Unbox successful. Sweet!
Commands:
Compile: truffle compile
Migrate: truffle migrate
Test contracts: truffle test
sily@lyg-sily:~/SmartContractDemo/HelloWorld$ ls
contracts migrations test truffle-config.js truffle.js
sily@lyg-sily:~/SmartContractDemo/HelloWorld$
在项目下的contracts/
文件夹中新建EncryptedToken.sol
,内容如下:
pragma solidity ^0.4.17;
contract EncryptedToken {
uint256 INITIAL_SUPPLY = 21000000;
mapping (address => uint256) balances;
function EncryptedToken() {
balances[msg.sender] = INITIAL_SUPPLY;
}
//转帐到一个指定的地址
function transfer(address _to, uint256 _amount) {
assert(balances[msg.sender] >= _amount);
balances[msg.sender] -= _amount;
balances[_to] += _amount;
}
//查看指定地址的余额
function balanceOf(address _owner) constant returns (uint256) {
return balances[_owner];
}
}
uint256 INITIAL_SUPPLY = 21000000
声明变量,将当前合约的钱包地址代币数设置为21000000。
mapping(address => uint256) balances;
声明了balances
是一个key
类型为address
、value
类型为uint256
的键值对(mapping)。
function EncryptedToken()
函数是EncryptedToken
合约的构造函数(contructor),当EncryptedToken
合约调用时,会先执行构造函数中的内容。在构造函数中,会以当前部署合约的钱包地址为key
,以INITIAL_SUPPLY
为value
初始化一个键值对。
transfer
函数声明了转账到指定钱包地址的函数,_to
是转账的目标地址,_amount
表示转账金额。
assert
断言确保当前钱包余额不少于要转账的金额时才能进行转账,否则会抛出异常。
balanceOf(address _owner)
函数是用来查询指定钱包地址的余额,_owner
即是指定的钱包地址,return (uint256)
代表返回值的类型是uint256
,constant
关键字说明了函数是只读的,调用时不会消耗手续费。
在migrations/
目录下创建一个3_deploy_contract.js
的文件。内容如下:
var EncryptedToken = artifacts.require("./EncryptedToken.sol");
module.exports = function(deployer) {
deployer.deploy(EncryptedToken);
}
然后我们执行truffle compile
和truffle migrate
命令:
sily@lyg-sily:~/SmartContractDemo/HelloWorld$ truffle compile
Compiling ./contracts/EncryptedToken.sol...
Compiling ./contracts/HelloWorld.sol...
Compiling ./contracts/Migrations.sol...
Compilation warnings encountered:
/home/sily/SmartContractDemo/HelloWorld/contracts/EncryptedToken.sol:6:3: Warning: No visibility specified. Defaulting to "public".
function EncryptedToken() {
^
Spanning multiple lines.
,/home/sily/SmartContractDemo/HelloWorld/contracts/EncryptedToken.sol:11:3: Warning: No visibility specified. Defaulting to "public".
function transfer(address _to, uint256 _amount) {
^
Spanning multiple lines.
,/home/sily/SmartContractDemo/HelloWorld/contracts/EncryptedToken.sol:18:3: Warning: No visibility specified. Defaulting to "public".
function balanceOf(address _owner) constant returns (uint256) {
^
Spanning multiple lines.
,/home/sily/SmartContractDemo/HelloWorld/contracts/HelloWorld.sol:4:3: Warning: Function state mutability can be restricted to pure
function sayHello() public constant returns (string) {
^
Spanning multiple lines.
,/home/sily/SmartContractDemo/HelloWorld/contracts/HelloWorld.sol:8:3: Warning: Function state mutability can be restricted to pure
function echo(string name) public constant returns (string) {
^
Spanning multiple lines.
Writing artifacts to ./build/contracts
sily@lyg-sily:~/SmartContractDemo/HelloWorld$ truffle migrate --reset
Using network 'development'.
Running migration: 1_initial_migration.js
Replacing Migrations...
... 0xfc5af4b443dcae96f96206e26d2bc93b9699e7d14980dd0247baea112bd04c44
Migrations: 0x9681bbaee9346afe05e7ceaf72b32d433b329090
Saving successful migration to network...
... 0x2155887e4c7037d33fbfbec1df073152ac72784a6a594886a066d412cf105b66
Saving artifacts...
Running migration: 2_deploy_contracts.js
Replacing HelloWorld...
... 0x9a4cd911a85438c4087ee7144dc2b0eeb9bf88f7985fcf56f9949b41b02f936a
HelloWorld: 0x644fad81bc1075e4f39dc765f671f81ad363317c
Saving successful migration to network...
... 0x19e5b97d493c652fc8f67c53c7a56899c8285127b2e70a1bfd41f7b8d35c067d
Saving artifacts...
Running migration: 3_deploy_contracts.js
Deploying EncryptedToken...
... 0x3206c798ab3d0e0ca5e49865bb0cbd207513c1907539c5c921cc027125639a4e
EncryptedToken: 0xeff776cc1aa2f01c40e638538fdfaadbfb179b00
Saving successful migration to network...
... 0x4fb48f957d364f77510f2acc82467dca4e70d268078db8418dfae78000523cd9
Saving artifacts...
合约部署完成后,输入truffle console
命令开启console
控制台。在控制台中对已部署的合约进行验证。
web3.eth.coinbase
或web3.eth.accounts[0]
表示testrpc
环境初始分配的首个钱包地址,也可以通过索引值获取其他9个钱包地址。
EncryptedToken.deployed().then(instance => contract = instance)
声明了合约变量contract
来存储EncryptedToken
合约实例。
sily@lyg-sily:~/SmartContractDemo/HelloWorld$ truffle console
truffle(development)> let contract;
undefined
truffle(development)> EncryptedToken.deployed().then(instance => contract = instance)
TruffleContract {
constructor:
{ [Function: TruffleContract]
_static_methods:
{ setProvider: [Function: setProvider],
new: [Function: new],
at: [Function: at],
deployed: [Function: deployed],
defaults: [Function: defaults],
hasNetwork: [Function: hasNetwork],
isDeployed: [Function: isDeployed],
detectNetwork: [Function: detectNetwork],
setNetwork: [Function: setNetwork],
resetAddress: [Function: resetAddress],
link: [Function: link],
clone: [Function: clone],
addProp: [Function: addProp],
toJSON: [Function: toJSON] },
_properties:
{ contract_name: [Object],
contractName: [Object],
abi: [Object],
network: [Function: network],
networks: [Function: networks],
address: [Object],
links: [Function: links],
events: [Function: events],
binary: [Function: binary],
deployedBinary: [Function: deployedBinary],
unlinked_binary: [Object],
bytecode: [Object],
deployedBytecode: [Object],
sourceMap: [Object],
deployedSourceMap: [Object],
source: [Object],
sourcePath: [Object],
ast: [Object],
compiler: [Object],
schema_version: [Function: schema_version],
schemaVersion: [Function: schemaVersion],
updated_at: [Function: updated_at],
updatedAt: [Function: updatedAt] },
_property_values: {},
_json:
{ contractName: 'EncryptedToken',
abi: [Array],
bytecode: '0x60606040526301406f40600055341561001757600080fd5b600054600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555061023e8061006c6000396000f30060606040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806370a0823114610051578063a9059cbb1461009e575b600080fd5b341561005c57600080fd5b610088600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506100e0565b6040518082815260200191505060405180910390f35b34156100a957600080fd5b6100de600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610129565b005b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b80600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015151561017457fe5b80600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555080600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555050505600a165627a7a723058205d5ccad9a01715b15d2733b795c870b25b04f5c79d10389a89ed6342e1e2b7ed0029',
deployedBytecode: '0x60606040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806370a0823114610051578063a9059cbb1461009e575b600080fd5b341561005c57600080fd5b610088600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506100e0565b6040518082815260200191505060405180910390f35b34156100a957600080fd5b6100de600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610129565b005b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b80600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015151561017457fe5b80600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555080600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555050505600a165627a7a723058205d5ccad9a01715b15d2733b795c870b25b04f5c79d10389a89ed6342e1e2b7ed0029',
sourceMap: '26:518:0:-;;;79:8;54:33;;132:74;;;;;;;;187:14;;164:8;:20;173:10;164:20;;;;;;;;;;;;;;;:37;;;;26:518;;;;;;',
deployedSourceMap: '26:518:0:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;446:96;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;245:165;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;446:96;499:7;521:8;:16;530:6;521:16;;;;;;;;;;;;;;;;514:23;;446:96;;;:::o;245:165::-;330:7;306:8;:20;315:10;306:20;;;;;;;;;;;;;;;;:31;;299:39;;;;;;368:7;344:8;:20;353:10;344:20;;;;;;;;;;;;;;;;:31;;;;;;;;;;;398:7;381:8;:13;390:3;381:13;;;;;;;;;;;;;;;;:24;;;;;;;;;;;245:165;;:::o',
source: 'pragma solidity ^0.4.17;\n\ncontract EncryptedToken {\n uint256 INITIAL_SUPPLY = 21000000;\n mapping (address => uint256) balances;\n function EncryptedToken() {\n balances[msg.sender] = INITIAL_SUPPLY;\n }\n\n //转帐到一个指定的地址\n function transfer(address _to, uint256 _amount) {\n assert(balances[msg.sender] >= _amount);\n balances[msg.sender] -= _amount;\n balances[_to] += _amount;\n }\n\n //查看指定地址的余额\n function balanceOf(address _owner) constant returns (uint256) {\n return balances[_owner];\n }\n}\n',
sourcePath: '/home/sily/SmartContractDemo/HelloWorld/contracts/EncryptedToken.sol',
ast: [Object],
compiler: [Object],
networks: [Object],
schemaVersion: '1.0.1',
updatedAt: '2018-01-11T08:57:36.557Z' },
setProvider: [Function: bound setProvider],
new: [Function: bound new],
at: [Function: bound at],
deployed: [Function: bound deployed],
defaults: [Function: bound defaults],
hasNetwork: [Function: bound hasNetwork],
isDeployed: [Function: bound isDeployed],
detectNetwork: [Function: bound detectNetwork],
setNetwork: [Function: bound setNetwork],
resetAddress: [Function: bound resetAddress],
link: [Function: bound link],
clone: [Function: bound clone],
addProp: [Function: bound addProp],
toJSON: [Function: bound toJSON],
web3:
Web3 {
_requestManager: [RequestManager],
currentProvider: [Provider],
eth: [Eth],
db: [DB],
shh: [Shh],
net: [Net],
personal: [Personal],
bzz: [Swarm],
settings: [Settings],
version: [Object],
providers: [Object],
_extend: [Function] },
class_defaults:
{ from: '0x73b5b6e153e03ec11eeeb3d623e50867a5e5227e',
gas: 6721975,
gasPrice: 100000000000 },
currentProvider:
HttpProvider {
host: 'http://localhost:8545',
timeout: 0,
user: undefined,
password: undefined,
send: [Function],
sendAsync: [Function],
_alreadyWrapped: true },
network_id: '1515660380238' },
abi:
[ { constant: true,
inputs: [Array],
name: 'balanceOf',
outputs: [Array],
payable: false,
stateMutability: 'view',
type: 'function' },
{ constant: false,
inputs: [Array],
name: 'transfer',
outputs: [],
payable: false,
stateMutability: 'nonpayable',
type: 'function' },
{ inputs: [],
payable: false,
stateMutability: 'nonpayable',
type: 'constructor' } ],
contract:
Contract {
_eth:
Eth {
_requestManager: [RequestManager],
getBalance: [Function],
getStorageAt: [Function],
getCode: [Function],
getBlock: [Function],
getUncle: [Function],
getCompilers: [Function],
getBlockTransactionCount: [Function],
getBlockUncleCount: [Function],
getTransaction: [Function],
getTransactionFromBlock: [Function],
getTransactionReceipt: [Function],
getTransactionCount: [Function],
call: [Function],
estimateGas: [Function],
sendRawTransaction: [Function],
signTransaction: [Function],
sendTransaction: [Function],
sign: [Function],
compile: [Object],
submitWork: [Function],
getWork: [Function],
coinbase: [Getter],
getCoinbase: [Function],
mining: [Getter],
getMining: [Function],
hashrate: [Getter],
getHashrate: [Function],
syncing: [Getter],
getSyncing: [Function],
gasPrice: [Getter],
getGasPrice: [Function],
accounts: [Getter],
getAccounts: [Function],
blockNumber: [Getter],
getBlockNumber: [Function],
protocolVersion: [Getter],
getProtocolVersion: [Function],
iban: [Function],
sendIBANTransaction: [Function: bound transfer] },
transactionHash: null,
address: '0xeff776cc1aa2f01c40e638538fdfaadbfb179b00',
abi: [ [Object], [Object], [Object] ],
balanceOf:
{ [Function: bound ]
request: [Function: bound ],
call: [Function: bound ],
sendTransaction: [Function: bound ],
estimateGas: [Function: bound ],
getData: [Function: bound ],
address: [Circular] },
transfer:
{ [Function: bound ]
request: [Function: bound ],
call: [Function: bound ],
sendTransaction: [Function: bound ],
estimateGas: [Function: bound ],
getData: [Function: bound ],
'address,uint256': [Circular] },
allEvents: [Function: bound ] },
balanceOf:
{ [Function]
call: [Function],
sendTransaction: [Function],
request: [Function: bound ],
estimateGas: [Function] },
transfer:
{ [Function]
call: [Function],
sendTransaction: [Function],
request: [Function: bound ],
estimateGas: [Function] },
sendTransaction: [Function],
send: [Function],
allEvents: [Function: bound ],
address: '0xeff776cc1aa2f01c40e638538fdfaadbfb179b00',
transactionHash: null }
接下来,调用contract合约的balanceOf
()函数和transfer
()函数,验证结果正确。
truffle(development)> contract.balanceOf(web3.eth.coinbase)
BigNumber { s: 1, e: 7, c: [ 21000000 ] }
truffle(development)> contract.balanceOf(web3.eth.accounts[1])
BigNumber { s: 1, e: 0, c: [ 0 ] }
truffle(development)> contract.trans
contract.transactionHash contract.transfer
truffle(development)> contract.transfer(web3.eth.accounts[1],100000000)
Error: VM Exception while processing transaction: invalid opcode
at Object.InvalidResponse (/home/sily/.nvm/nvm/versions/node/v9.3.0/lib/node_modules/truffle/build/cli.bundled.js:41484:16)
at /home/sily/.nvm/nvm/versions/node/v9.3.0/lib/node_modules/truffle/build/cli.bundled.js:329530:36
at /home/sily/.nvm/nvm/versions/node/v9.3.0/lib/node_modules/truffle/build/cli.bundled.js:325200:9
at XMLHttpRequest.request.onreadystatechange (/home/sily/.nvm/nvm/versions/node/v9.3.0/lib/node_modules/truffle/build/cli.bundled.js:328229:7)
at XMLHttpRequestEventTarget.dispatchEvent (/home/sily/.nvm/nvm/versions/node/v9.3.0/lib/node_modules/truffle/build/cli.bundled.js:176415:18)
at XMLHttpRequest._setReadyState (/home/sily/.nvm/nvm/versions/node/v9.3.0/lib/node_modules/truffle/build/cli.bundled.js:176705:12)
at XMLHttpRequest._onHttpResponseEnd (/home/sily/.nvm/nvm/versions/node/v9.3.0/lib/node_modules/truffle/build/cli.bundled.js:176860:12)
truffle(development)> contract.transfer(web3.eth.accounts[1],100)
{ tx: '0x5de889a31a95a7971439c54659b91f95dce1103359527ad270cc8d507246c339',
receipt:
{ transactionHash: '0x5de889a31a95a7971439c54659b91f95dce1103359527ad270cc8d507246c339',
transactionIndex: 0,
blockHash: '0x430cd8a197cd900e20374d14e20ad655fa82d456ca31e896c937e3f2cb6b9005',
blockNumber: 12,
gasUsed: 49056,
cumulativeGasUsed: 49056,
contractAddress: null,
logs: [],
status: 1 },
logs: [] }
truffle(development)> contract.balanceOf(web3.eth.accounts[0])
BigNumber { s: 1, e: 7, c: [ 20999900 ] }
truffle(development)> contract.balanceOf(web3.eth.accounts[1])
BigNumber { s: 1, e: 2, c: [ 100 ] }
truffle(development)>
以上就是基于智能合约建立最简单的具有转账和查询钱包地址余额功能的代币教程。但是,逻辑中并没有对安全相关的操作进行处理,比如当余额不够时或者地址不合法等。
文章参考:
http://blog.csdn.net/liyuechun520/article/details/78049950