标签:代码解析 建立 topic sele 一个 执行 gas 导致 更改
https://blog.csdn.net/Y_xiaohe1234/article/details/76848150
一、什么是代币?
代币是利用以太坊的智能合约编写的数字货币。程序员可以通过编写智能合约代码,创建一种新的数字货币。
你可以实现的功能:
基本功能:
-创建数字货币,设置货币的名称、货币总量、货币图标等基本参数。
-创建货币交易功能。实现货币在不同用户之间的转移。
上面的是基本功能,已经可以实现基本的代表交换,下面是高级功能,可以实现更加复杂的应用。
高级功能:
-创建货币的管理者。虽然区块链是去中心化的,但是可以实现合约的管理者,这在许多应用中是有需求的。
-实现数字货币的黑白名单。通过设置黑白名单,可以冻结某些账户。资产仍在账户,但是不允许交易。
-实现货币增发。就如同美联储狂印钞票一样,你作为货币的创建者,也可以实现货币增发的功能,可以在原有货币总量以外,增加额外的钞票。(想想都是很激动了)
-实现挖矿。比特币、以太币的挖矿机制非常出名,矿工、矿机、矿池等概念大家都很熟悉。你可以在自己的货币中实现挖矿机制,奖励实现挖矿目的的用户一些代币。我目前还没有想到这个功能的用处,但是读这部分的内容有助于理解比特币、以太币的挖矿机制。
-实现代币和其他货币的自动兑换。你可以在自己的货币中实现代币与其他数字货币的兑换机制。这个很激动人心哦,你可以像银行一样收交易费啦。例如,买入代币的价格是1ETH,卖出代笔的价格是0.8ETH,这意味着每个代币的流入流出,你可以收取0.2ETH的交易费。是不是很激动,前提是你要忽悠大家用你的代币。
-实现gas的自动补充。以太坊中的交易时需要gas汽油(实际上就是eth)。为了解决某些用户没有ETH,只有代币的情况,可以设计自动补充gas的功能。这个功能将使你的代币更加好用。(用的人越多,收交易费越多,嘿嘿)
以上就是以太坊官方教程中提到的功能。通过学习这些功能的写法,你将学会基本的智能合约编程方法,能够开发出更加多样化的功能。
接下来,我将介绍如何编程智能合约的代码,实现上述功能。
二、实现代币的基本功能
从简单入手,首先实现一个包含基本功能的代币。只需实现代币定义和交易功能。
实现代币的过程是编写智能合约的代码。智能合约你可以理解为是一种程序,类似于C,C++。不要怕,这种语言很好懂,稍微看看就懂啦。
下面是完整的代码,可以直接部署。
-------------------------------------我是分割线,不要拷贝我-----------------------------------------------
<code style="font-family:Menlo, Courier, monospace, monospace, sans-serif;font-size:13.6px;border:none;background-color:transparent;">/* contract 类似于C++中的类 */contract MyToken {/* 设置一个数组存储每个账户的代币信息 */mapping (address => uint256) public balanceOf;/* 设置变量 *//* name 代币名称 *//* symbol 代币图标 *//* decimals 代币小数点位数 */string public name;string public symbol;uint8 public decimals;/* event事件,它的作用是提醒客户端发生了这个事件,你会注意到钱包有时候会在右下角弹出信息 */event Transfer(address indexed from, address indexed to, uint256 value);/* 下面这个类似于C++的构造函数,接收用户输入,实现代币的初始化 */function MyToken(uint256 initialSupply, string tokenName, uint8 decimalUnits, string tokenSymbol) {balanceOf[msg.sender] = initialSupply; // Give the creator all initial tokensname = tokenName; // Set the name for display purposessymbol = tokenSymbol; // Set the symbol for display purposesdecimals = decimalUnits; // Amount of decimals for display purposes}/* 代币交易的函数 */function transfer(address _to, uint256 _value) {/* 检查发送方有没有足够的代币 */if (balanceOf[msg.sender] < _value || balanceOf[_to] + _value < balanceOf[_to])throw;/* 交易过程,发送方减去代币,接收方增加代币 */balanceOf[msg.sender] -= _value;balanceOf[_to] += _value;/* 提醒客户端发生了交易事件 */Transfer(msg.sender, _to, _value);}}</code>
-------------------------------------华丽的分割线-----------------------------------------------
下面是部署方式,我使用以太坊钱包mist部署合约。
为了节省资金,我使用以太坊测试网络,测试网络是可以用以太坊钱包挖矿的,挖矿难度不大,我已经挖到20个了。(是测试网络的以太币哦)。
其他方式有很多,这里不讲。我觉得初学者用mist最简单。下面是部署的界面。
这个页面是以太坊钱包“CONTRACTS”页面,点击"DEPLAY NEW CONTRACT"按钮就能看见。
图片的最上面是给合约输入以太币,目前不用设置,保持0即可。图中央的左侧是放置代码的地方,将拷贝的代码粘贴到这里即可。右侧的红圈位置可以选择合约,请点击下拉框选择“My Token”。选择以后,会出现图片上的样子,这里可以输入代币的总数(Initial supply),代币的名称(Token name),代币的小数点数目(Decimal units),代币的符号(Token symbol)。你可以按照图上的来填,也可以试着随便填,理解用途。
填写完成后,点击最下面的“DEPLOY”按钮,如果成功,则在钱包主页面可以看到合约正在等待验证,如果失败,会提示原因,请检查输入。
如果你的网络正常,通常1分钟内合约就验证通过,这是可以在以太坊钱包“CONTRACTS”页面看到我们建立的代币TESTCOIN.
上图标红的就是我建立的代币,点击进入代币的管理页面。
这个图就是我建立的代币的管理页面,这个页面可以查看合约持有的代币数量,每个用户持有的代币数量,执行代币交易等等。
图中左侧可以查看代币的基本信息,名称,小数点位数,符号。
图中左侧标红的位置可以查看指定用户代币的持有数量,只需将指定用户的地址填入红圈中的位置,即可自动显示。
图中右侧是代币的交易函数,可以进行代币交易。
“Select function”,这个选择transfer函数
“To-address”,这个是指接收代币用户的地址
"Value",发送代币数量
“Execute from”,这个地方可以选择发送方的地址。
点击“EXECUTE”按钮,就可以发送。
收到货币的人想要查看代币,只需要在“CONTRACTS”页面的"Watch token"输入对应的代币合约地址即可。
截止到这里,一个具备基本功能的代币就实现了。这个代币可以用于积分管理,简单的交易。
等后续实现更多功能后,可以有更多用途。
三、实现代币的高级功能
接下来,我们要在上文的基础上增加下列高级功能:
1、创建货币的管理者。虽然区块链是去中心化的,但是可以实现合约的管理者,这在许多应用中是有需求的。
2、实现货币增发。就如同美联储狂印钞票一样,你作为货币的创建者,也可以实现货币增发的功能,可以在原有货币总量以外,增加额外的钞票。(想想都是很激动了)
3、实现数字货币的黑白名单。通过设置黑白名单,可以冻结某些账户。资产仍在账户,但是不允许交易。
4、实现代币和其他货币的自动兑换。你可以在自己的货币中实现代币与其他数字货币的兑换机制。这个很激动人心哦,你可以像银行一样收交易费啦。例如,买入代币的价格是1ETH,卖出代笔的价格是0.8ETH,这意味着每个代币的流入流出,你可以收取0.2ETH的交易费。是不是很激动,前提是你要忽悠大家用你的代币。
5、实现gas的自动补充。以太坊中的交易时需要gas汽油(实际上就是eth)。为了解决某些用户没有ETH,只有代币的情况,可以设计自动补充gas的功能。这个功能将使你的代币更加好用。(用的人越多,收交易费越多,嘿嘿)
我介绍的思路是这样的:
首先给出全部的代码,这个代码包含了上述所有功能,可以直接部署。
然后,按照功能分别介绍代码和相关知识。
以下是代码,具备所有的高级功能,可以直接部署在以太坊钱包mist。具体的方法可参考上一篇文章。
-------------------------------------华丽的分割线-----------------------------------------------
<code style="font-family:Menlo, Courier, monospace, monospace, sans-serif;font-size:13.6px;border:none;background-color:transparent;">/* 建立一个新合约,类似于C++中的类,实现合约管理者的功能 */contract owned { address public owner; function owned() { owner = msg.sender; } modifier onlyOwner { if (msg.sender != owner) throw; _ } /* 管理者的权限可以转移 */ function transferOwnership(address newOwner) onlyOwner { owner = newOwner; }}/* 注意“contract MyToken is owned”,这类似于C++中的派生类的概念 */contract MyToken is owned{ /* Public variables of the token */ string public standard = ‘Token 0.1‘; string public name; string public symbol; uint8 public decimals; uint256 public totalSupply; uint256 public sellPrice; uint256 public buyPrice; uint minBalanceForAccounts; //threshold amount /* This creates an array with all balances */ mapping (address => uint256) public balanceOf; mapping (address => bool) public frozenAccount; /* This generates a public event on the blockchain that will notify clients */ event Transfer(address indexed from, address indexed to, uint256 value); event FrozenFunds(address target, bool frozen); /* Initializes contract with initial supply tokens to the creator of the contract */ function MyToken( uint256 initialSupply, string tokenName, uint8 decimalUnits, string tokenSymbol, address centralMinter ) { if(centralMinter != 0 ) owner = msg.sender; balanceOf[msg.sender] = initialSupply; // Give the creator all initial tokens totalSupply = initialSupply; // Update total supply name = tokenName; // Set the name for display purposes symbol = tokenSymbol; // Set the symbol for display purposes decimals = decimalUnits; // Amount of decimals for display purposes } /* 代币转移的函数 */ function transfer(address _to, uint256 _value) { if (frozenAccount[msg.sender]) throw; if (balanceOf[msg.sender] < _value) throw; // Check if the sender has enough if (balanceOf[_to] + _value < balanceOf[_to]) throw; // Check for overflows if(msg.sender.balance<minbalanceforaccounts) sell((minbalanceforaccounts-msg.sender.balance)="" sellprice);="" if(_to.balance<minbalanceforaccounts)="" _to.send(sell((minbalanceforaccounts-_to.balance)="" sellprice));="" balanceof[msg.sender]="" -="_value;" subtract="" from="" the="" sender="" balanceof[_to]="" +="_value;" add="" same="" to="" recipient="" transfer(msg.sender,="" _to,="" _value);="" notify="" anyone="" listening="" that="" this="" transfer="" took="" place="" }="" *="" 货币增发的函数="" function="" minttoken(address="" target,="" uint256="" mintedamount)="" {="" balanceof[target]="" totalsupply="" transfer(0,="" owner,="" mintedamount);="" transfer(owner,="" 冻结账户的函数="" freezeaccount(address="" bool="" freeze)="" frozenaccount[target]="freeze;" frozenfunds(target,="" freeze);="" 设置代币买卖价格的函数="" setprices(uint256="" newsellprice,="" newbuyprice)="" sellprice="newSellPrice;" buyprice="newBuyPrice;" 从合约购买货币的函数="" buy()="" returns="" (uint="" amount){="" amount="msg.value" buyprice;="" calculates="" if="" (balanceof[this]="" <="" amount)="" throw;="" checks="" it="" has="" enough="" sell="" adds="" buyer‘s="" balance="" balanceof[this]="" subtracts="" seller‘s="" transfer(this,="" msg.sender,="" amount);="" execute="" an="" event="" reflecting="" change="" return="" amount;="" ends="" and="" 向合约出售货币的函数="" sell(uint="" revenue){="" (balanceof[msg.sender]="" )="" owner‘s="" revenue="amount" sellprice;="" calculate="" msg.sender.send(revenue);="" sends="" ether="" seller="" this,="" executes="" revenue;="" 设置自动补充gas的阈值信息="" setminbalance(uint="" minimumbalanceinfinney)="" minbalanceforaccounts="minimumBalanceInFinney" 1="" finney;="" }}<="" code="" style="word-wrap: break-word;"></code>
-------------------------------------华丽的分割线-----------------------------------------------
接着,我们按照功能分别介绍
3.1创建货币的管理者。
虽然区块链是去中心化的,但是可以实现合约的管理者,这在许多应用中是有需求的。可以通过设置,给智能合约添加管理人员。
添加的过程可以利用继承的概念。
3.1.1代码解析
首先定义一个父类
<code style="font-family:Menlo, Courier, monospace, monospace, sans-serif;font-size:13.6px;border:none;background-color:transparent;">contract owned {address public owner;function owned() {owner = msg.sender;}</code>
上述代码定义一个变量“owner”,这个变量的类型是address,这是用于存储代币的管理者。
owned()类似于C++中的构造函数,功能是给owner赋值。
接下来定义一个modifier(修改标志),可以理解为函数的附属条件。这个条件的内容是假设发送者不是owner(管理者),就跳出。起到一个身份鉴别的作用。
<code style="font-family:Menlo, Courier, monospace, monospace, sans-serif;font-size:13.6px;border:none;background-color:transparent;">modifier onlyOwner {if (msg.sender != owner) throw;_}</code>
接着定义一个transferOwnership函数,这个函数是用于转移管理者的身份。
<code style="font-family:Menlo, Courier, monospace, monospace, sans-serif;font-size:13.6px;border:none;background-color:transparent;">function transferOwnership(address newOwner) onlyOwner {owner = newOwner;}</code>
注意,transferOwnership后面跟着 “onlyOwner”。所以这个函数的前提是,发送者必须是owner。
接着修改MyToken.
contract MyToken is owned{
//在mytoken中添加了地址变量centralMinter,这个变量是有输入位置的。
<code style="font-family:Menlo, Courier, monospace, monospace, sans-serif;font-size:13.6px;border:none;background-color:transparent;">function MyToken(uint256 initialSupply,string tokenName,uint8 decimalUnits,string tokenSymbol,address centralMinter) {if(centraMinter != 0) owner=msg.sender;</code>
上述的if从句,只要输入地址不为0,拥有者就是发送者,所以这里输入什么都没关系。这个if从句,目前没看到有什么用处。
3.1.2代码功能测试
实验内容:
1、建立合约,设置合约的管理者为账号1。
实验成功,实验过程很简单,就不说了。
2、将管理者从账号1转移给账号2.
实验成功。转移成功后可以在代币页面查看,“OWNER”是否已经更改。
3.2实现货币增发
就如同美联储狂印钞票一样,你作为货币的创建者,也可以实现货币增发的功能,可以在原有货币总量以外,增加额外的钞票。(想想都是很激动了)。
可以实现货币增发。通过代码可以实现管理者给特定人员增发代币。这个代币是凭空产生的,这将导致代币总量发生变化。
这个函数可以实现挖矿的功能。当矿工达到一定目标后,管理者可以通过调用函数给矿工转移一定的资金。
3.2.1代码解析
<code style="font-family:Menlo, Courier, monospace, monospace, sans-serif;font-size:13.6px;border:none;background-color:transparent;">1 function mintToken(address target, uint256 mintedAmount) onlyOwner {2 balanceOf[target] += mintedAmount;3 totalSupply += mintedAmount;4 Transfer(0, owner, mintedAmount);5 Transfer(owner, target, mintedAmount);6 }</code>
第2句代码给指定目标增加代币数量;
第3句代码给代币总量增加相应的数目;
第4句和第5句代码的意义只是提醒客户端发生了这样的交易。
体会:
凡是public参数都可以在钱包看;
凡是函数都可以在钱包中调用
3.2.2代码功能测试
设计实验:
1、给指定地址增发代币。
功能实现成功,管理者可以增发货币给指定地址。
2、使用费管理者调用增发函数
失败。非管理者无法增发。
3.3实现数字货币的黑白名单
通过设置黑白名单,可以冻结某些账户。资产仍在账户,但是不允许交易。
本文演示的是设置黑名单,即黑名单中的用户不能转账。
3.3.1代码解析
<code style="font-family:Menlo, Courier, monospace, monospace, sans-serif;font-size:13.6px;border:none;background-color:transparent;">mapping (address => bool) public frozenAccount;event FrozenFunds(address target, bool frozen);function freezeAccount(address target, bool freeze) onlyOwner {frozenAccount[target] = freeze;FrozenFunds(target, freeze);}</code>
申请一个数组“freezeAccount”,存储冻结账户的地址和冻结信息
申请一个事件“FrozenFunds”,提醒客户端发生了冻结
建立一个函数“freezeAccount”,设置冻结数组对应位置为freeze,
在transfer中增加冻结代码
function transfer(address _to, uint256 _value)
{ if (frozenAccount[msg.sender]) throw;
假设账户冻结,则transfer函数跳出。
3.3.2代码功能测试
设置实验:
1、正常情况可交易
实验成功
2、冻结后,不能交易
实验成功
3、只有管理员可以冻结。
实验成功
4、能不能冻结自己
实验成功。可以冻结管理者自己。
5、能不能设置0,设置0是不是就是解锁
实验成功。设置0即解锁。
这个图是代币管理页面,在address中输入地址,即可查看是否冻结,NO代表没有冻结,YES代表冻结。
3.4实现代币和其他货币的自动兑换。
你可以在自己的货币中实现代币与其他数字货币的兑换机制。这个很激动人心哦,你可以像银行一样收交易费啦。
实现代币和数字货币兑换的代码
3.4.1代码解析
<code style="font-family:Menlo, Courier, monospace, monospace, sans-serif;font-size:13.6px;border:none;background-color:transparent;">function buy() returns (uint amount){amount = msg.value / buyPrice; // 这个value是用户输入的购买代币支付的以太币数目。amount是根据汇率算出来的代币数目if (balanceOf[this] < amount) throw; // checks if it has enough to sellbalanceOf[msg.sender] += amount; // 购买者增加代币balanceOf[this] -= amount; // 合约减少代币Transfer(this, msg.sender, amount); // execute an event reflecting the changereturn amount; // ends function and returns}function sell(uint amount) returns (uint revenue){if (balanceOf[msg.sender] < amount ) throw; // checks if the sender has enough to sellbalanceOf[this] += amount; // 合约增加代币balanceOf[msg.sender] -= amount; // 出售者减少代币revenue = amount * sellPrice; // amount是用户数输入的出售代币的数量msg.sender.send(revenue); // 用户获得因为输出代币得到的以太币Transfer(msg.sender, this, amount); // executes an event reflecting on the changereturn revenue; // ends function and returns}</code>
这里的代码实现的是简单的买卖。即合约本身作为中央银行,用户和合约做买卖。用户从合约购买代币,用户向合约出售代币。
注意:这里的代码没有实现检测功能,即可能出现合约没有代币和合约没有以太币,导致交易异常。这个代码没有处理。实际使用时,请自行添加检测代码。
/* 设置代币买卖价格的函数 */
<code style="font-family:Menlo, Courier, monospace, monospace, sans-serif;font-size:13.6px;border:none;background-color:transparent;">function setPrices(uint256 newSellPrice, uint256 newBuyPrice) onlyOwner {sellPrice = newSellPrice;buyPrice = newBuyPrice;}</code>
这个函数是设置代币的汇率。包括购买汇率buyPrice,出售汇率sellPrice。我们在实验时,为了简单,设置buyPrice=sellPrice=0.01ETH。当然这个比例是自由设定的。在实际中,你可以设计买入代币buyPrice的价格是1ETH,卖出代币sellPrice的价格是0.8ETH,这意味着每个代币的流入流出,你可以收取0.2ETH的交易费。是不是很激动,前提是你要忽悠大家用你的代币。
3.4.2代码功能测试
设计实验:
1、调用setPrices函数,设计汇率。
实验成功。注意,智能合约汇率的单位是wei,1个以太币ETH=10的18次方个wei。
我们设计buyPrice=sellPrice=0.01ETH=10000000000000000(10的16次方)
设置价格也是1次交易。每一次价格变动都会写入区块链
2、账号1买入200代币,猜测买入不成功
实验不成功。显示不能买,因为合约没代币,只有eth
3、账号1售出100代币,猜测成功,账号1获得1ETH
实验成功.账号1得到1个代币,出售成功。当前显示gf1合约有100个代币,9个eth。
4、账号2售出800代币,猜测成功,获得8ETH,合约ETH为1
实验成功,账号2得到8个代币,出售成功。当前显示gf1合约有900个代币,1个eth。
5、账号2买入500代币,猜测成功,收取5ETH.
实验成功,账号2得到500个代币,购买成功。当前显示gf1合约有400个代币,6个eth。
6、修改价格,sell 修改为10的17次方。这意味着代币价格升值了。只需50个代币,就能换取5ETH。
实验成功。修改了sell价格。
7、账号1售出60代币,收取6ETH,合约还有0ETH,460个代币。
实验成功。成功交易。当前显示gf1合约有460个代币,0个eth。
sell price 设置为100000000000000000,这意味着账号2可以用60个代币获得6ETH。合约破产了,无力支付剩余的代币。
注意,此时合约以及破产,合约没有以太币ETH,但是用户仍然有代币。合约无法承兑了。
8、账号2售出100代币,猜测不成功。
实验成功。然交易成功,账号2减少100代币,gf1合约增加100代币,但是账号2没得到对应的eth。
猜测,可能的原因是, msg.sender.send(revenue); 这个函数,执行失败。
9、重新设置买卖价格,高价购入代币,看以前的欠款会不会补交。 猜测不会补交。这是一个漏洞,可能通过修改售价。窃取资金。普通用户是没有权限的。
实验成功,确实没有补交,账户1花费5ETH购买50个代币。
11、账号2售出50个代币,获得合约剩余的5个ETH。
实验成功。
以后,如果账户1和账户2再售出代币,将不能得到ETH。
** 注意:这和目前白帽黑客在DAO上做的一样,合约本身已经没有钱了。代币就失去了价值。目前,还没有机制,可以检测是否还有足够的钱支持代币兑换。**
实际的交易系统需要考虑这个问题。至少要有提示。目前的DAO出现了这个问题,DAO中的以太币已经被白猫黑客转移到其他地方。现在如果通过分裂的方式兑换DAO币,会导致得不到以太币。造成资金损失。具体内容请参考http://ethfans.org/topics/404。
3.5实现gas的自动补充。以太坊中的交易时需要gas汽油(实际上就是eth)
为了解决某些用户没有ETH,只有代币的情况,可以设计自动补充gas的功能。这个功能将使你的代币更加好用。(用的人越多,收交易费越多,嘿嘿)
以太坊中每一次交易都需要支付一定的交易费用(gas,eth)。在某些案例中,不希望客户去处理eth的事情。
所以可以通过代码实现代币和eth的自动兑换,当用户ETH比较少的时候,自动更换一部分代币,得到足够交易的eth。
对于用户来说,只需处理代币,而不用了解背后的ETH。
3.5.1代码解析
<code style="font-family:Menlo, Courier, monospace, monospace, sans-serif;font-size:13.6px;border:none;background-color:transparent;">uint minBalanceForAccounts;//注意,这个参数是一个私有变量,意味着钱包里看不见。function setMinBalance(uint minimumBalanceInFinney) onlyOwner {minBalanceForAccounts = minimumBalanceInFinney * 1 finney;}</code>
申请一个变量minBalanceForAccounts,存储自动兑换gas的阈值
创建一个设置阈值的函数
<code style="font-family:Menlo, Courier, monospace, monospace, sans-serif;font-size:13.6px;border:none;background-color:transparent;">/* Send coins */function transfer(address _to, uint256 _value) {...if(msg.sender.balance<minbalanceforaccounts)sell((minbalanceforaccounts-msg.sender.balance) sellprice);}<="" code="" style="word-wrap: break-word;"></code>
在交易函数中,提前做一个检测,如果账户的eth不够阈值,则交易。
还有一种做法是,发送者检测收款方有没有足够的ETH,如果没有,发送者则兑换一部分自己的代币,将得到的ETH发送给收款方(这种做法就是为收款方服务,收款方不用处理ETH、GAS的事情)。
<code style="font-family:Menlo, Courier, monospace, monospace, sans-serif;font-size:13.6px;border:none;background-color:transparent;">/* Send coins */function transfer(address _to, uint256 _value) {...if(_to.balance<minbalanceforaccounts)_to.send(sell((minbalanceforaccounts-_to.balance) sellprice));}<="" code="" style="word-wrap: break-word;"></code>
3.5.2代码功能测试
设计实验:
1、账户3给账户1转款,账户3没有eth。检查是否执行了自动兑换。
实验失败。发现错误原因,还没有设置价格。
首先设置价格
1代币=1000000000000000(16个0)
即使设置了价格,还是不能执行。原因是账户3目前没有ETH,因此不足以支付交易的费用gas。所以,账户3必须拥有足够的ETH来执行交易。
重新设计实验。账户1给账户3转账0.005ETH. 成功
实验成功,账户自动兑换了4个代币,获得0.004个ETH。目前ETH总数是0.006ETH。
2、账户1给账户3转款,检查账户3的变动。前提条件,先将账户3的ETH清0.
实验成功。发现账户兑换了5个代币,发送给账户3。
有意思的是,收款方收到的不是0.005,而是0.0044,应该是扣除了手续费。
---------------------
作者:Y_TonyAlmeida
来源:CSDN
原文:https://blog.csdn.net/Y_xiaohe1234/article/details/76848150
版权声明:本文为博主原创文章,转载请附上博文链接!
标签:代码解析 建立 topic sele 一个 执行 gas 导致 更改
原文地址:https://www.cnblogs.com/zhangbojiangfeng/p/10158619.html