本文主要介绍了如何使用truffle + Atom
进行以太坊公开透明拍卖智能合约的编写,以及如何使用ganache-cli
进行智能合约的交互测试。
1 Trueffle框架编写代码
1.1 建立项目
新建项目文件夹SimpleAuction
开启另一个终端窗口,输入以下命令建立项目:
PS H:\TestContract> mkdir SimpleAuction
目录: H:\TestContract
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 2018/7/12 12:01 SimpleAuction
PS H:\TestContract> cd SimpleAuction
PS H:\TestContract\SimpleAuction> truffle init
Downloading...
Unpacking...
Setting up...
Unbox successful. Sweet!
Commands:
Compile: truffle compile
Migrate: truffle migrate
Test contracts: truffle test
PS H:\TestContract\SimpleAuction>
目录结构:
打开contracts
文件夹,再通过truffle命令创建SimpleAuction
合约,必须通过命令行创建。
PS H:\TestContract\SimpleAuction> cd contracts
PS H:\TestContract\SimpleAuction\contracts> truffle create contract SimpleAuction
-
\contracts
:存放智能合约源代码的地方,可以看到里面已经有一个sol
文件,我们开发的BlindAuction.sol
文件就存放在这个文件夹。 -
\migrations
:这是Truffle
用来部署智能合约的功能,待会儿我们会新建一个类似1_initial_migration.js
的文件来部署BlindAuction.sol
。 -
\test
:测试智能合约的代码放在这里,支持js
与sol
测试。 -
truffle-config.js
和truffle.js
:Truffle
的配置文件,需要配置要连接的以太坊网络。
1.2 创建合约
在Atom中打开项目文件夹SimpleAuction
,开始编辑contracts
文件夹下的SimpleAuction.sol
文件。
整个流程如下:
- 我们首先要记录拍卖的基本数据:谁是受益人,什么时候结束
- 我们开启拍卖,一个出价更高的人会替代之前出价最高的人
- 当出现替代时,还要退还之前出价高的人的代币
- 出于安全的考虑,退还过程将由之前用户主动发起
pragma solidity ^0.4.22;
contract SimpleAuction {
// 定义参数:受益人、拍卖结束时间
address public beneficiary;
uint public auctionEnd;
uint public biddingTime;
uint public auctionStart;
// 最高出价者
address public highestBidder;
// 最高出价
uint public highestBid;
mapping (address => uint) pendingReturns; // 用于取回之前的出价
// 拍卖是否结束,不允许被修改
bool ended;
// 最高出价变动时调用事件
event HighestBidIncreased(address _bidder, uint _amount);
// 拍卖结束时调用事件
event AuctionEnded(address _winner, uint _amount);
// The following is a so-called natspec comment,
// recognizable by the three slashes.
// It will be shown when the user is asked to
// confirm a transaction.
// 构造函数
// 创建一个拍卖对象,初始化参数值:受益人、拍卖持续时间
constructor(uint _biddingTime, address _beneficiary) public {
beneficiary = _beneficiary;
auctionStart = now;
biddingTime = _biddingTime;
auctionEnd = now + _biddingTime; // now: current block's timestamp
}
// 使用代币进行拍卖,当拍卖失败时,会退回代币
// 出价功能:包括交易参数
// 当出价不是最高,资金会被自动退回
function bid() public payable{
// 不需要参数,因为都被自动处理了
// 当一个函数要处理Ether时,需要包含payable的修饰符
// 如果超过了截止期,交易撤回
if(now > auctionStart + biddingTime){
revert();
}
// 如果出价不够,交易撤回
if (msg.value <= highestBid){
revert();
}
// 如果出价最高,当前出价者作为最高出价人
if (highestBidder != 0){
//highestBidder.send(highestBid); // send ether(in wei)to the address
// 调用highestBidder.send(highestBid)的方式是危险的
// 因为会执行不知道的协议
// 因此最好让用户自己取回自己的代币
pendingReturns[highestBidder] += highestBid;
}
highestBidder = msg.sender;
highestBid = msg.value;
emit HighestBidIncreased(msg.sender, msg.value);
}
// 取回被超出的拍卖前的出资
function withdraw() public returns (bool){
uint amount = pendingReturns[msg.sender];
if (amount > 0){
// 需要提前设置为0,因为接收者可以在这个函数结束前再次调用它
pendingReturns[msg.sender] = 0;
if (!msg.sender.send(amount)){
// 不需要throw,直接重置代币数量即可
pendingReturns[msg.sender] = amount;
return false;
}
}
return true;
}
// 结束拍卖,将金额给予受益人
function auctionEnd() public {
// 与其他协议交互的最好遵循以下顺序的三个步骤:
// 1.检查状况
// 2.修改状态
// 3.合约交互
// 如果这三个步骤混在一起,那么攻击者可能通过多次调用这个函数来进行攻击
// 1.检查状况
if (now <= auctionEnd) {
revert();
}
if(ended){
revert();
}
// require (now >= auctionEnd, "Auction not yet ended.");
// require (!ended, "auctionEnd has already called.");
// 2.修改状态
ended = true;
emit AuctionEnded(highestBidder, highestBid);
// 3.合约交互
beneficiary.transfer(highestBid);
}
function () public{
revert();
}
}
Note:一定要有最后的function()
不然调用bid()
总会报错。
1.3 编译合约
在项目根目录SimpleAuction
的powershell中执行truffle compile
命令:
PS H:\TestContract\SimpleAuction> truffle compile
Compiling .\contracts\Migrations.sol...
Compiling .\contracts\SimpleAuction.sol...
Compilation warnings encountered:
/H/TestContract/SimpleAuction/contracts/SimpleAuction.sol:32:5: Warning: Defining constructors as functions with the same name as the contract is deprecated. Use "constructor(...) { ... }" instead.
function SimpleAuction(uint _biddingTime, address _beneficiary) public {
^ (Relevant source part starts here and spans across multiple lines).
Writing artifacts to .\build\contracts
PS H:\TestContract\SimpleAuction>
2 Ganache-cli 部署测试智能合约
2.1 启动ganache-cli
打开powershell
终端,可以看到ganache-cli
启动后自动建立了10
个账号(Accounts),与每个账号对应的私钥(Private Key)。每个账号中都有100
个测试用的以太币(Ether)。
Note. ganache-cli仅运行在内存中,因此每次重开时都会回到全新的状态。
C:\Users\aby>ganache-cli
Ganache CLI v6.1.6 (ganache-core: 2.1.5)
Available Accounts
==================
(0) 0x553cc75f3099ec4f6ba57e2580f65f982dfcbc67 (~100 ETH)
(1) 0xed700e53205af0b9daa4548cc48465cab55d376c (~100 ETH)
(2) 0x3fdcbf82e2343f3d88a5ec967800ee188b7bc440 (~100 ETH)
(3) 0x76757998260e9d7bb01e2761a4abacffd73162e2 (~100 ETH)
(4) 0x0e53c9c2cb04a7b46acd3e94703434c6e0b1e1c9 (~100 ETH)
(5) 0xfb7db3e68877e08df576d0071d1f68b1ad185d50 (~100 ETH)
(6) 0x0ff87726fac78f3675751fc67c3b2b174aa7ec68 (~100 ETH)
(7) 0x8bee12196d694c864b00c4b19295b0c0a067bb1a (~100 ETH)
(8) 0x14e498d9ca25b050e17fb702e1325135da21e9e7 (~100 ETH)
(9) 0x1aed5d4a441d50716313426bdc519b8263c37bfc (~100 ETH)
Private Keys
==================
(0) 0x260ed471ee7c4897c3b478a3ae36fb47f7b5dc4e2bfaeea2062e53edbc664f8e
(1) 0xbbaaf6be4797da3598ca72969b46a04569448a7d67185f96b55b2618327176b6
(2) 0xca9fd89f5c5ada96b35afd2b5782c14b2286894454178e9225df2aa3bed133d4
(3) 0x5e282a3ddb12f4f77318a30f2e653b1764b3fac52f43d575e5b1cd9564e6c6f6
(4) 0x17f336fbcc161a20f38979d787eb9f1ae33fea870acdd37142dc3be3bcf127e7
(5) 0x9521e42f1d3a03079883032a47a7146cd76275bf0bf206b5f0467c8a2249e553
(6) 0x4fd49f71649d35d9d93d5c9be03f554844ee68a7c45ae9c899f08ead8d2e0405
(7) 0x0221b4004e5b70684a0d19926739d5ed2e420d9e7c908a734763fbbf51d13133
(8) 0xd54daf5bfda3d3491ff5b3cefbba6a9e88b68d8ae322135337e9d530523308a6
(9) 0x73420d353ed74fd5939590dc30c2e7a966727f6561c84526ce71f04e0c668bec
HD Wallet
==================
Mnemonic: give sick brand tail farm mechanic fence flock submit boost fiction magnet
Base HD Path: m/44'/60'/0'/0/{account_index}
Gas Price
==================
20000000000
Gas Limit
==================
6721975
Listening on 127.0.0.1:8545
2.2 部署合约
(1)migrations
目录下创建一个名字叫做2_deploy_contracts.js
的文件。文件中的内容为:
var SimpleAuction = artifacts.require('./SimpleAuction.sol');
module.exports = function(deployer) {
deployer.deploy(SimpleAuction);
}
(2)修改truffle.js
文件,连接本地ganache-cli
环境。参数在最开初始化ganache-cli
环境的窗口可以看到。
module.exports = {
// See <http://truffleframework.com/docs/advanced/configuration>
// to customize your Truffle configuration!
networks: {
development:{
host: "127.0.0.1",
port: 8545,
network_id: "*" // match any network id
}
}
};
(3)现在执行truffle migrate
命令,我们可以将SimpleAuction.sol
原始码编译成Ethereum bytecode
。
PS H:\TestContract\SimpleAuction> truffle migrate
Using network 'development'.
Running migration: 2_deploy_contracts.js
Deploying SimpleAuction...
Error encountered, bailing. Network state unknown. Review successful transactions manually.
Error: SimpleAuction contract constructor expected 2 arguments, received 0
at C:\Users\aby\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\truffle-contract\contract.js:390:1
at new Promise (<anonymous>)
at C:\Users\aby\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\truffle-contract\contract.js:374:1
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:188:7)
PS H:\TestContract\SimpleAuction>
发现报错,Error: SimpleAuction contract constructor expected 2 arguments, received 0
,可以在部署时进行构造函数的赋值,不必修改智能合约内容:在2_deploy_contracts.js
中,修改deploy脚本,deployer.deploy(SimpleAuction, 20, "0x553cc75f3099ec4f6ba57e2580f65f982dfcbc67");
即可。
PS H:\TestContract\SimpleAuction> truffle migrate
Using network 'development'.
Running migration: 2_deploy_contracts.js
Deploying SimpleAuction...
... 0x3b611b5e12c85ba20b59c66b7a2b997a580fab7dbe1431be323fcdd3f9d07f62
SimpleAuction: 0x7307036020a33ae1996ff74a5044123eda03302c
Saving successful migration to network...
... 0xb646aa61fefa4933f6102d04e7af232c10c0c43f9384df49ab250f54a083c2c6
Saving artifacts...
PS H:\TestContract\SimpleAuction>
2.3 与合约交互
truffle
提供命令行工具,执行truffle console
命令后,可用Javascript
来和刚刚部署的合约互动。
PS H:\TestContract\SimpleAuction> truffle console
truffle(development)>
使用web3.eth.accounts
会输出ganache-cli
网络上的所有账户。
truffle(development)> web3.eth.accounts
[ '0x553cc75f3099ec4f6ba57e2580f65f982dfcbc67',
'0xed700e53205af0b9daa4548cc48465cab55d376c',
'0x3fdcbf82e2343f3d88a5ec967800ee188b7bc440',
'0x76757998260e9d7bb01e2761a4abacffd73162e2',
'0x0e53c9c2cb04a7b46acd3e94703434c6e0b1e1c9',
'0xfb7db3e68877e08df576d0071d1f68b1ad185d50',
'0x0ff87726fac78f3675751fc67c3b2b174aa7ec68',
'0x8bee12196d694c864b00c4b19295b0c0a067bb1a',
'0x14e498d9ca25b050e17fb702e1325135da21e9e7',
'0x1aed5d4a441d50716313426bdc519b8263c37bfc' ]
我们需要准备一些测试账户。
它会把第一个帐户的地址分配给变量account0
,第二个帐户分配给变量account1
。Web3
是一个JavaScript API
,它将RPC
调用包装起来以方便我们与区块链进行交互。
truffle(development)> acc1 = web3.eth.accounts[1]
'0xed700e53205af0b9daa4548cc48465cab55d376c'
我们可以看一下拍卖发起人以及第一个账户的余额:
truffle(development)> web3.eth.getBalance(address)
BigNumber { s: 1, e: 19, c: [ 999060, 70900000000000 ] }
truffle(development)> web3.eth.getBalance(acc1)
BigNumber { s: 1, e: 20, c: [ 1000000 ] }
此时我们用acc1
调用bid()
,发送2 ether
。
truffle(development)> contract.bid({from:acc1, value:web3.toWei(2,"ether")})
{ tx: '0xfad44c38347ee56220bf3810c4e6904b6c1e10a3cbd6495da9008533ffc5a1ee',
receipt:
{ transactionHash: '0xfad44c38347ee56220bf3810c4e6904b6c1e10a3cbd6495da9008533ffc5a1ee',
transactionIndex: 0,
blockHash: '0xd9657a80fd84cb9a6582f560f3ce91dcc7f571932772d7f4ce4dd60ecd0f25c9',
blockNumber: 5,
gasUsed: 63946,
cumulativeGasUsed: 63946,
contractAddress: null,
logs: [ [Object] ],
status: '0x1',
logsBloom: '0x00004000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000' },
logs:
[ { logIndex: 0,
transactionIndex: 0,
transactionHash: '0xfad44c38347ee56220bf3810c4e6904b6c1e10a3cbd6495da9008533ffc5a1ee',
blockHash: '0xd9657a80fd84cb9a6582f560f3ce91dcc7f571932772d7f4ce4dd60ecd0f25c9',
blockNumber: 5,
address: '0x4faf92d14a7ac059b4fb97975376f52c32e1abe1',
type: 'mined',
event: 'HighestBidIncreased',
args: [Object] } ] }
并且查看此时acc1
余额,以及highestBid
。
truffle(development)> contract.highestBid.call()
BigNumber { s: 1, e: 18, c: [ 20000 ] }
truffle(development)> web3.eth.getBalance(acc1)
BigNumber { s: 1, e: 19, c: [ 979936, 5400000000000 ] }
由于拍卖时间设置较短,所以结束拍卖。
truffle(development)> contract.auctionEnd({from:address})
{ tx: '0x8701d85470423c67608d14eaa96e807a156981bf9d47826aa0bfcd69b4711fcc',
receipt:
{ transactionHash: '0x8701d85470423c67608d14eaa96e807a156981bf9d47826aa0bfcd69b4711fcc',
transactionIndex: 0,
blockHash: '0x5dc6e9b00d495f19b2e3427b128c5b70544191a2bf8c78d9deabf6a79deaf097',
blockNumber: 8,
gasUsed: 51912,
cumulativeGasUsed: 51912,
contractAddress: null,
logs: [ [Object] ],
status: '0x1',
logsBloom: '0x00004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000001000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000' },
logs:
[ { logIndex: 0,
transactionIndex: 0,
transactionHash: '0x8701d85470423c67608d14eaa96e807a156981bf9d47826aa0bfcd69b4711fcc',
blockHash: '0x5dc6e9b00d495f19b2e3427b128c5b70544191a2bf8c78d9deabf6a79deaf097',
blockNumber: 8,
address: '0x4faf92d14a7ac059b4fb97975376f52c32e1abe1',
type: 'mined',
event: 'AuctionEnded',
args: [Object] } ] }
2.4 拍卖过程
由于上面结束拍卖,我们现在重新设置拍卖时长,并连贯顺序地进行一次拍卖过程。由于ganache-cli
每次都重新启动,都会随机创建10个账户,所以这里地址不同。
同样创建其他账户
PS H:\TestContract\SimpleAuction> truffle console
truffle(development)> address = web3.eth.accounts[0]
'0x0bf5cd9a0313121dda0f91e2da6ff9479be4ec5c'
truffle(development)> acc1 = web3.eth.accounts[1]
'0xdfa9eca806498bcf1c083b14ab20c53b2a3815a5'
truffle(development)> acc2 = web3.eth.accounts[2]
'0xc8dd1ff47b6e7067f1b4c2ec18fc60f36da97c46'
truffle(development)> acc3 = web3.eth.accounts[3]
'0xf758b41d6bb008e6f64bea810ecc38be46299ae6'
truffle(development)> acc4 = web3.eth.accounts[4]
'0x0830f17d845b90d8dc965ddb73b43e0f46e06bb8'
address
调用创建一个拍卖:
truffle(development)> let contract
undefined
truffle(development)> SimpleAuction.deployed().then(instance => contract = instance)
我们可以看一下,当前创建拍卖的收益人,最高价,以及各个账户的余额等。
truffle(development)> contract.beneficiary.call()
'0x0bf5cd9a0313121dda0f91e2da6ff9479be4ec5c'
truffle(development)> contract.auctionStart.call()
BigNumber { s: 1, e: 9, c: [ 1531385059 ] }
truffle(development)> contract.highestBid.call()
BigNumber { s: 1, e: 0, c: [ 0 ] }
truffle(development)> contract.highestBidder.call()
'0x0000000000000000000000000000000000000000'
truffle(development)> web3.eth.getBalance(address)
BigNumber { s: 1, e: 19, c: [ 999060, 70900000000000 ] }
truffle(development)> web3.eth.getBalance(acc1)
BigNumber { s: 1, e: 20, c: [ 1000000 ] }
truffle(development)> web3.eth.getBalance(acc2)
BigNumber { s: 1, e: 20, c: [ 1000000 ] }
使用acc1
进行一次bid()
:
truffle(development)> contract.bid({from:acc1, value:web3.toWei(2,"ether")})
{ tx: '0x68d752b6afc9b1bbb5c987bf4c4faad3907ea312f552f9280573a22dca662f05',
receipt:
{ transactionHash: '0x68d752b6afc9b1bbb5c987bf4c4faad3907ea312f552f9280573a22dca662f05',
transactionIndex: 0,
blockHash: '0x30f1edf3689d8af7639c2bf2475e932d34155891d333512e3b97dffac25f5220',
blockNumber: 5,
gasUsed: 63946,
cumulativeGasUsed: 63946,
contractAddress: null,
logs: [ [Object] ],
status: '0x1',
logsBloom: '0x00000000000000000000000000800080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' },
logs:
[ { logIndex: 0,
transactionIndex: 0,
transactionHash: '0x68d752b6afc9b1bbb5c987bf4c4faad3907ea312f552f9280573a22dca662f05',
blockHash: '0x30f1edf3689d8af7639c2bf2475e932d34155891d333512e3b97dffac25f5220',
blockNumber: 5,
address: '0x55ac96c388568a6d2e233a8dbb9c1e5be1c3e4c8',
type: 'mined',
event: 'HighestBidIncreased',
args: [Object] } ] }
查看当前acc1
的余额,以及highestBid
,可以看到acc1
减少了,highestBid
变成了2000
:
truffle(development)> web3.eth.getBalance(acc1)
BigNumber { s: 1, e: 19, c: [ 979936, 5400000000000 ] }
truffle(development)> contract.highestBid.call()
BigNumber { s: 1, e: 18, c: [ 20000 ] }
同样的,使用acc2
,acc3
,acc4
分别进行bid
:
Account | Value |
---|---|
acc1 | 2 |
acc2 | 4 |
acc3 | 1 |
acc4 | 6 |
可以看到当bid
价格比highestBid
高时会出现上面的结果,如果低,则会被revert()
抛出异常。
truffle(development)> contract.bid({from:acc3, value:web3.toWei(1,"ether")})
Error: VM Exception while processing transaction: revert
at XMLHttpRequest._onHttpResponseEnd (C:\Users\aby\AppData\Roaming\npm\node_modules\truffle\build\webpack:\~\xhr2\lib\xhr2.js:509:1)
at XMLHttpRequest._setReadyState (C:\Users\aby\AppData\Roaming\npm\node_modules\truffle\build\webpack:\~\xhr2\lib\xhr2.js:354:1)
at XMLHttpRequestEventTarget.dispatchEvent (C:\Users\aby\AppData\Roaming\npm\node_modules\truffle\build\webpack:\~\xhr2\lib\xhr2.js:64:1)
at XMLHttpRequest.request.onreadystatechange (C:\Users\aby\AppData\Roaming\npm\node_modules\truffle\build\webpack:\~\web3\lib\web3\httpprovider.js:128:1)
at C:\Users\aby\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\truffle-provider\wrapper.js:134:1
at C:\Users\aby\AppData\Roaming\npm\node_modules\truffle\build\webpack:\~\web3\lib\web3\requestmanager.js:86:1
at Object.InvalidResponse (C:\Users\aby\AppData\Roaming\npm\node_modules\truffle\build\webpack:\~\web3\lib\web3\errors.js:38:1)
此时,调用结束拍卖函数auctionEnd()
,也会被revert()
抛出异常,因为当前时间小于结束时间auctionEnd
。
过一段时间,再次调用auctionEnd()
:
truffle(development)> contract.auctionEnd.sendTransaction({from:address})
'0xfe1ed3d6fb181d2f0bb122edcadb99c735abee31f4d620f25a7e8ca7c8677014'
再看看此时受益者,也就是address
地址的余额:
可以明显看到他增加了6 ether
,得到收益。
truffle(development)> web3.eth.getBalance(address)
BigNumber { s: 1, e: 20, c: [ 1058987, 14600000000000 ] }
最后每个报价者取回自己的余额,竞拍失败者可以取回钱。
truffle(development)> contract.withdraw({from:acc1})
{ tx: '0x44d9f709c52d2e2b27e0d075b2afbd3fb9f065a1d66b70026a6657bd48a3c1ef',
receipt:
{ transactionHash: '0x44d9f709c52d2e2b27e0d075b2afbd3fb9f065a1d66b70026a6657bd48a3c1ef',
transactionIndex: 0,
blockHash: '0xa5b1f3a0e2670e88462bd1c6338fe4eaa1afc6496a81fc508899fedc86d38e8a',
blockNumber: 11,
gasUsed: 19482,
cumulativeGasUsed: 19482,
contractAddress: null,
logs: [],
status: '0x1',
logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' },
logs: [] }
acc1
查看自己当前账户余额:
truffle(development)> web3.eth.getBalance(acc1)
BigNumber { s: 1, e: 19, c: [ 999916, 57200000000000 ] }
其他账户做同样操作。
2.5 账户变化
最后可以总结一下,每个参与拍卖的账户余额变化:
账户 | 1.初始化 | 2.创建合约 | 3.bid | 4.拍卖结束及取回 |
---|---|---|---|---|
address | 1000000 | 999060, 70900000000000 | 999060, 70900000000000 | 1058987, 14600000000000 |
acc1 | 1000000 | 1000000 | 979936, 5400000000000(2) | 999916, 57200000000000 |
acc2 | 1000000 | 1000000 | 4 | 999925, 81300000000000 |
acc3 | 1000000 | 1000000 | 1 | 999956, 8300000000000 |
acc4 | 1000000 | 1000000 | 6 | 939923, 41400000000000 |
由于发起交易需要消耗gas
所以,账户1,2,3余额会比最开始少一点。而acc4
则因为拍卖成功,相对较少的较多。
本文作者:Joyce
文章来源:https://www.jianshu.com/p/65c93265ad8e
版权声明:转载请注明出处!
2018年7月12日
参考:一个使用Atom,truffle,ganache-cli创建部署智能合约的方法智能合约开发之 Hello World!。