本篇文章,我们将写一个简易的加密代币的智能合约来给大家诠释加密代币的原理
启动 ganache-cli
打开终端,启动ganache-cli
,相关环境在区块链学习日记(四)这篇文章里面已经有具体说明。
wangsanjundeMacBook-Pro:~ wangsanjun$ ganache-cli
Ganache CLI v6.0.3 (ganache-core: 2.0.2)
Available Accounts
==================
(0) 0x79221bc486bcc9380d280c4221856f7122b32d6f
(1) 0xc72f1267309aaa7aa79c1f6b971edca111e0c3e4
(2) 0x5139a3ca66f06102eabbb38989021bca9ade63db
(3) 0x20cb6137afeb2da01d2fb28ade7822a5ffe214e1
(4) 0x93382d64508ca98b9f2a7dbf13366be157be04fa
(5) 0x051a305c1d016391cea8d9d19c5560d0bb4148f4
(6) 0x9676763004ec23beab44feeb6d9d9f86a846b7ab
(7) 0xe0400ae3a7390d78397d12fc0b768f8209f5abf4
(8) 0xd17a5a1a9b26d35ef957bf5686db90b011f8d9a8
(9) 0x0b529a02f4f6c997dae911a64fce47d4a850b77c
Private Keys
==================
(0) 85e35b54c141ebe2df61cc56192aced3ba03f51e2ae8ca0ed299a25ab8ac7155
(1) 4215839182c1ffcb5f8854631fd1a2372be3b3910a0f193e392f90df85befb03
(2) 90081b0b6f3ffc6ce6245bed5f0f864982522d65a4b47a891f07b6be0deec03b
(3) 845efda127b31c0c0e4ef142bb62de73dc89bf3269d1ace9e3be3ad2476c29ff
(4) dfa540af69e16a81415e362295e9526acb4189eb883bbf30a843c2289a2f7cb0
(5) 18fac89e76063d582881ad98a6ad6f79e07782b64bbac507034ccb5a66cc5e7e
(6) 3e0ab00795f1f061f7ca681e64d51bd1ee59427bfe5477e751ee93f9596d95b6
(7) a09f85a55ee46a61030b87bea045cff8e43671287709608bd329f41e33660442
(8) 420ec6889ba6aa5ec1cfd1757b9209687350487cc14dc56e8792b2bd6727de4a
(9) 45e96bbd340ef850ee32b1f3a5808ed3aecad889f1929377fe6eabd95c0dcc77
HD Wallet
==================
Mnemonic: neither mountain become final nephew drill ramp onion attract parade neck merge
Base HD Path: m/44'/60'/0'/0/{account_index}
Listening on localhost:8545
...
接下来我们就可以一步步的创建我们的加密代币项目了。
代币合约的基本概念
代币合约扮演的角色相当于银行的角色。使用者在代币合约中,用自己的以太币帐户地址
当作银行帐户,可以透过代币合约执行转账(transfer
,将代币由一个帐户转到另一个帐户),查询余额(balanceOf
,查询指定帐户中拥有的代币)等原本由银行负责的工作。因为合约部署在公开区块链上,所有的交易都是公开透明,可供检验的。
创建代币合约项目
wangsanjundeMacBook-Pro:SmartContractDemo wangsanjun$ mkdir EncrptedToken
wangsanjundeMacBook-Pro:EncrptedToken wangsanjun$ truffle init
Downloading...
Unpacking...
Setting up...
Unbox successful. Sweet!
Commands:
Compile: truffle compile
Migrate: truffle migrate
Test contracts: truffle test
wangsanjundeMacBook-Pro:EncrptedToken wangsanjun$
新建代币合约
终端执行truffle create contract EncryptedToken
命令创建EncryptedToken.sol
合约。
编写合约代码
将下面的合约代码拷贝,替换EncryptedToken.sol
文件的代码。
pragma solidity ^0.4.17;
contract EncryptedToken {
uint256 INITIAL_SUPPLY = 666666;
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];
}
}
pragma solidity ^0.4.17
中的0.4.17
代表solidity
的版本,^
代表0.4.17 ~ 0.4.99
之间的solidity
都可以正常编译当前版本的合约。
contract
相当于其他语言中的class
,EncryptedToken
相当于类
的名字。contract EncryptedToken
可以理解为class EncryptedToken extends Contract
。
uint256 INITIAL_SUPPLY = 666666
声明了一个变量INITIAL_SUPPLY
,初始化存储了一个666666
的整数作为部署当前合约的钱包地址的代币数。
mapping(address => uint256) balances;
,balances
是一个key
类型为address
,value
类型为uint256
的键值对 (mapping),相当于 Java 中的map
、iOS 中的NSDictionary
。
function EncryptedToken()
函数是EncryptedToken
合约的构造函数 (contructor),当EncryptedToken
合约调用时,会先执行它的构造函数。
在构造函数中,会以当前部署合约的钱包地址为key
,以INITIAL_SUPPLY
为value
初始化一个键值对。
function transfer(address _to, uint256 _amount) {
assert(balances[msg.sender] >= _amount);
balances[msg.sender] -= _amount;
balances[_to] += _amount;
}
transfer
函数是声明用来从转账到指定钱包地址的函数,_to
代表转账的目的地地址,_amount
代表转账金额。
assert(balances[msg.sender] >= _amount)
,这句代码中,我声明了一个断言,当balances[msg.sender] >= _amount
,即当前钱包余额小于要转账的额度时,就会抛出异常。
balances[msg.sender] -= _amount;
从当前钱包额度中减去_amount
。
balances[_to] += _amount;
,将目标地址的额度增加_amount
。
function balanceOf(address _owner) constant returns (uint256) {
return balances[_owner];
}
balanceOf(address _owner)
函数是用来查询指定钱包地址的余额,_owner
即是指定的钱包地址,returns (uint256)
代表返回值的类型为uint256
,constant
关键字的作用是,当我们调用balanceOf
函数时,它会自动调用call()
方法,表明是只读数据,而不需要往区块链写入数据,调用这个方法,不需要花费手续费。
编译与部署
在migrations/
目录下创建一个名字叫做2_deploy_contract.js
的文件。文件中的内容为:
var EncryptedToken = artifacts.require('./EncryptedToken.sol');
module.exports = function(deployer) {
deployer.deploy(EncryptedToken);
}
修改truffle.js
文件,连接本地ganache-cli环境
module.exports = {
networks: {
development: {
host: "127.0.0.1",
port: 8545,
network_id: "*" // Match any network id
}
}
};
接下来执行compile
和migrate
命令:
wangsanjundeMacBook-Pro:EncrptedToken wangsanjun$ truffle compile
Compiling ./contracts/EncryptedToken.sol...
Compiling ./contracts/Migrations.sol...
Compilation warnings encountered:
/Users/wangsanjun/Desktop/SmartContractDemo/EncrptedToken/contracts/EncryptedToken.sol:6:3: Warning: No visibility specified. Defaulting to "public".
function EncryptedToken() {
^
Spanning multiple lines.
,/Users/wangsanjun/Desktop/SmartContractDemo/EncrptedToken/contracts/EncryptedToken.sol:10:3: Warning: No visibility specified. Defaulting to "public".
function transfer(address _to, uint256 _amount) {
^
Spanning multiple lines.
,/Users/wangsanjun/Desktop/SmartContractDemo/EncrptedToken/contracts/EncryptedToken.sol:16:3: Warning: No visibility specified. Defaulting to "public".
function balanceOf(address _owner) constant returns (uint256) {
^
Spanning multiple lines.
Writing artifacts to ./build/contracts
wangsanjundeMacBook-Pro:EncrptedToken wangsanjun$ truffle migrate
Using network 'development'.
Running migration: 1_initial_migration.js
Deploying Migrations...
... 0xc4e64f8636e83fe67a9c0044667cf19237e631e801dd682f7ef01c8175eb7d6c
Migrations: 0x252a66c521dd81312455411acdd8074c159ee300
Saving successful migration to network...
... 0x0c2c60f492f7a82ab7d72e523b75f024ae83f402be313d033078d46b49555a89
Saving artifacts...
Running migration: 2_deploy_contract.js
Deploying EncryptedToken...
... 0x14675402f0781db21afccb69a11a3e2177d06d7eba4a95b67a1b146caf48262e
EncryptedToken: 0x5c6d0f9528791e07350e00e80e66e655583d7b69
Saving successful migration to network...
... 0x447a94584192424ff4401892c5f478db7d39218895e856636e578ed536e838e1
Saving artifacts...
wangsanjundeMacBook-Pro:EncrptedToken wangsanjun$
如上所示,我们已经将EncryptedToken
代币合约部署到了ganache-cli
上。
合约验证
合约部署完成后,我们通过truffle console
开启console
控制台,在这个控制台中对已经部署的合约进行验证。
接下来声明一个合约变量存储EncryptedToken
合约实例。
truffle(development)> let contract;
undefined
truffle(development)> EncryptedToken.deployed().then(instance => contract = instance)
.....
truffle(development)>
验证web3.eth.coinbase
和web3.eth.accounts[1]
中的余额。
truffle(development)> contract.balanceOf(web3.eth.coinbase)
{ [String: '666666'] s: 1, e: 5, c: [ 666666 ] }
truffle(development)> contract.balanceOf(web3.eth.accounts[1])
{ [String: '0'] s: 1, e: 0, c: [ 0 ] }
truffle(development)>
经验证,第 0 个钱包地址中的代币余额为666666
,第 1 个钱包地址中的代币余额为0
。
下一步,我们将从第 0 个账号中向第 1 个账号转账666
个代币。
truffle(development)> contract.transfer(web3.eth.accounts[1],666)
{ tx: '0x50c71bd9a522afb9ff054bf11755d7665d7d9770f1bfca3548973df7aace24ab',
receipt:
{ transactionHash: '0x50c71bd9a522afb9ff054bf11755d7665d7d9770f1bfca3548973df7aace24ab',
transactionIndex: 0,
blockHash: '0x2fc452564dad406210c26be62e44770c6fa6ec1f435ff488ffb393a5317b8de6',
blockNumber: 28,
gasUsed: 49120,
cumulativeGasUsed: 49120,
contractAddress: null,
logs: [],
status: 1 },
logs: [] }
truffle(development)> contract.balanceOf(web3.eth.coinbase)
{ [String: '666000'] s: 1, e: 5, c: [ 666000 ] }
truffle(development)> contract.balanceOf(web3.eth.accounts[1])
{ [String: '666'] s: 1, e: 2, c: [ 666 ] }
truffle(development)>
如上所示,代币转账成功。
备注:如果合约部署成功之后,又修改了代码。重新编译时,一定要先将build
文件夹删除,其次在部署合约时,一定要添加--reset
,否则修改后的合约没法部署成功。