我们拿上一篇文章的示例程序作为切入点,来了解一下solidity的主要语法。需要说明的是这篇文章不是solidity的手册,不会把语言所有的语法点都覆盖,需要查看手册可以看文章最后的参考链接。
上一篇文章我们的实例程序如下,这是一个简单的模拟银行的智能合约。
代码语言:javascript复制pragma solidity 0.6.6;
contract SimpleBank {
mapping (address => uint) private balances;
address public owner;
event LogDepositMade(address accountAddress, uint amount);
constructor() public {
owner = msg.sender;
}
function deposit() public payable returns (uint) {
require((balances[msg.sender] msg.value) >= balances[msg.sender]);
balances[msg.sender] = msg.value;
emit LogDepositMade(msg.sender, msg.value); // fire event
return balances[msg.sender];
}
function withdraw(uint withdrawAmount) public returns (uint remainingBal) {
require(withdrawAmount <= balances[msg.sender]);
balances[msg.sender] -= withdrawAmount;
msg.sender.transfer(withdrawAmount);
return balances[msg.sender];
}
function balance() view public returns (uint) {
return balances[msg.sender];
}
}
第一行,pragma solidity 0.6.6
指明了我们的代码使用的是solidity的0.6.6版本,也可以写成下面的形式:
pragma solidity >=0.4.16 <0.9.0;
这个表示下面的代码适用的solidity版本是大于等于0.4.16,但是要小于0.9.0。
这一行其实是给编译器看的,让编译器使用正确的版本编译我们的代码。
接下来的contract
那一行,有点像我们在其他编程语言用的class关键字,声明我们接下来的代码是个contract。contract可以认为是一个代码(code)和数据(data)的集合。
接下来是mapping (address => uint) private balances
,这是一个key-value类型,key是address类型(下面讲),value是uint类型。这里声明了一个mapping类型的变量balance表示余额。private表示这个变量只能在合约内部访问,在合约外部或者派生合约都不能访问。除了private,还有public,external和internal这几个类型。
- public 内部外部都可以调用,会自动生成getter函数
- internal 和private类似,区别在于派生合约
- external 定义的外部函数可以被其它合约调用。用 external 修饰的外部函数 function() 不能作为内部函数直接调用
下面一行,
代码语言:javascript复制address public owner
address是一种比较特殊的类型,它有20个字节长度,一般用来表示地址或者账户的公钥信息。需要注意的是,虽然看起来它是一个数字的类型,但是它不支持任何的算术运算操作。比如 和-。
这个owner用来表示合约持有者的地址。
也可以是下面这样的基本类型,
代码语言:javascript复制 uint storedData;
string value;
接下来一行event LogDepositMade(address accountAddress, uint amount);
,然后在下面有一个调用,
emit LogDepositMade(msg.sender, msg.value);
这里首先是声明了一个事件,然后通过emit触发事件的执行。有事件一般就有对用的监听者(listeners),比如我们可以使用web3.js(这个后面会讲)来监听,示例如下:
代码语言:javascript复制SimpleBank.LogDepositMade().watch({}, '', function(error, result) {
if (!error) {
console.log("deposit to : " result.args.accountAddress
" amount " result.args.amount);
}
})
constructor
是构造方法,这个比较好理解,其他编程语言一般也有这个。构造方法只会在程序启动的时候调用一次,这里是把owner
变量赋值为msg.sender
,后者是一个约定的内部变量,表示的是方法的调用者。在这里当然就是指合约的创建人。
deposit
是一个方法,表示存钱。这里首先需要我们关注的是payable
这个关键字。被这个关键字修饰的方法可以在调用的时候接收ETH。这个方法的首先检查发送的ETH值要必须大于0,然后更新余额,出发日志事件,最后返回余额。
withdraw
方法的逻辑是取钱。逻辑也很简单,不多说。balance
方法就是返回余额,有一个关键字view
,表示这是一个只读的方法,调用这种方法不消耗GAS。
参考:
- https://docs.soliditylang.org/en/latest/