大家好,通学技术,学通技术,欢迎大家继续跟我一起学习智能合约系列。
上节内容的最后,我们提出了一个问题,msg.value
是什么?
不多卖关子,在solidity
中,我们称之为全局变量
,这里与我们java,python等语言中全局变量不同,更像是以太坊系统提供给我们的一种系统变量,以方便我们更好地进行转账交易或查询区块链信息等操作。
这里我们重点介绍几个比较重要的以太坊全局变量
。
pragma solidity ^0.4.16;
contract globleTest{
function getSender() view returns(address){
return msg.sender;
}
function getDifficulty() view returns(uint){
return block.difficulty;
}
function getBlockNumber() view returns(uint){
return block.number;
}
function getCoinbase() view returns(address){
return block.coinbase;
}
}
编译部署执行方法后,我们会得到以下结果:
这里上面主要说了四个全局变量,分别是获取发送者地址、获取当前区块的难度
获取挖出当前区块的矿工地址,获取区块高度。我们后面会经常用到。我几个概念我们可以先简单理解为:
关于以太坊的全局变量,我们这里罗列一下,大家可以自行测试。
block.blockhash(uint blockNumber) returns (bytes32):指定区块的区块哈希——仅可用于最新的 256 个区块且不包括当前区块;而 blocks 从 0.4.22 版本开始已经不推荐使用,由 blockhash(uint blockNumber) 代替
block.coinbase (address): 挖出当前区块的矿工地址
block.difficulty (uint): 当前区块难度
block.gaslimit (uint): 当前区块 gas 限额
block.number (uint): 当前区块号
block.timestamp (uint): 自 unix epoch 起始当前区块以秒计的时间戳
gasleft() returns (uint256):剩余的 gas
msg.data (bytes): 完整的 calldata
msg.gas (uint): 剩余 gas - 自 0.4.21 版本开始已经不推荐使用,由 gesleft() 代替
msg.sender (address): 消息发送者(当前调用)
msg.sig (bytes4): calldata 的前 4 字节(也就是函数标识符)
msg.value (uint): 随消息发送的 wei 的数量
now (uint): 目前区块时间戳(block.timestamp)
tx.gasprice (uint): 交易的 gas 价格
tx.origin (address): 交易发起者(完全的调用链)
在上面的内容中,具体为2.2.1节及4.4.2节中,我们介绍了两种转账方式,并且说明了两者没有什么差别。这里我们把这个内容再放到一起对比下。
pragma solidity ^0.4.16;
contract payableTest{
// callable函数
function () payable{
}
// 方式一 可以直接通过外部输入给合约转账 等同于方式二
function pay() payable{
}
// 方式二 通过外部输入给合约转账 等同于方式一
// 注意:使用this关键字 必须写callback函数
function pay2() payable{
this.transfer(msg.value);
}
// 方式三 直接给合约转账
function pay3() payable{
this.transfer(10 ether);
}
// 方式四 给其他账户转账
function pay4() payable{
address account = 0x14723a09acff6d2a60dcdf7aa4aff308fddc160c;
// account.transfer(10 ether);
account.transfer(msg.value);
}
}
这里我们可以看到方式一、二、三都可以给合约账户进行转账,而方式四则可以指定账户进行转账。
不过在给其他账户进行转账的时候,我们需要注意一点 ,下面来看看:
// 方式五 给其他账户转账 (慎用 容易引起误操作)
// 必须给运行界面—>交易金额 设置初始转账金额 不能为0
function pay5() payable{
address account = 0x14723a09acff6d2a60dcdf7aa4aff308fddc160c;
account.transfer(10 ether);
}
如果运行界面->交易金额为0时,会报错如下:
transact to payableTest.pay5 errored: VM error: revert.
revert The transaction has been reverted to the initial state.
Note: The constructor should be payable if you send value. Debug the transaction to get more information
如果我们设置了初始交易金额 ,则会得到以下结果:
相信小伙伴们,经过这一小节的练习,转账操作理解会更深刻一些。
我们上节内容可以看到,在我们调用transfer
方法时,如果说外部输入的交易金额 为0时,则会发生报错。而这里有一个不报错的方法,你想不想用呢?
function sendTest() payable{
address account = 0x14723a09acff6d2a60dcdf7aa4aff308fddc160c;
account.send(10 ether);
}
但是呢?send
方法在执行时会有一些风险:
所以使用send
方法,要检查成功与否,我们可以根据其返回值是true
还是false
进行后续操作,但不建议。
function sendTest() payable returns(bool){
address account = 0x14723a09acff6d2a60dcdf7aa4aff308fddc160c;
return account.send(10 ether);
}
transfer
相对于send
方法来说,更加安全,推荐使用。正所谓印证了那句话,人生苦短,我用transfer
.
这节课内容,视频所举的例子,个人觉得有些牵强,所以按照一下思路,进行讲解。
经过前面的学习,我们都知道,我们运行环境里面的当前账号,就相当于一个数字钱包地址。在以太坊上,我们都可以有一个唯一的钱包地址,那么怎么把这个钱包和我们的身份关联起来呢?在solidity
的世界里,我们的身份又怎么和我们的姓名关联起来呢?
如果有小伙伴接触过java
,肯定都学过hashMap
,面试的必考点之一。我们知道HashMap
是可以通过key-value
的形式去描述一种绑定关系,那么在solidity
的世界里,是不是也存在这样一种数据结构呢?
答案是显然的。就是我们的mapping
.
pragma solidity ^0.4.16;
contract mappingTest{
// 映射 一个钱包地址对应一个个人身份id
mapping(address => uint) idMapping;
// 映射 一个个人身份id地址对应一个个人姓名
mapping(uint => string) nameMapping;
uint id = 1;
// 给某某人提供以太钱包
function offerWallet(string name) {
address account = msg.sender;
// 先给某某人分配一个身份id,然后将钱包绑定到某某人的身份id上
idMapping[account] = id;
// 然后在将个人id与个人姓名进行映射绑定
nameMapping[id] = name;
id++;
}
// 根据地质获取身份id
function getIdByAddress(address account) view returns(uint){
return idMapping[account];
}
// 根据id获取个人姓名
function getNameById(uint id) view returns(string){
return nameMapping[id];
}
}
编译部署后,我们依次执行以下方法,会得到下图所示:
至此,就解决我们前面提出的问题啦。