solidity自学手册
官⽹:http://solidity.readthedocs.io/en/develop/
Solidity是⼀个⾯向对象的⾼级语⾔,其语法是类JavaScript,是运⾏在以太坊虚拟机中的代码,它是静态类型的编程语⾔。
版本申明
pragma solidity ^0.4.0;
^0.4.0代表solidity编译器的版本号,第⼆位的4是主版本,第三位的4是
⼩版本,^符号表示版本向上兼容,即 0.4.4 ~ 0.5.0(不包含0.5.0) 之间的solidity均可编译该合约代码,⼀般修
复bug时会更新⼩版本,⼤调整时修改主版本
全局引⼊
import “filename”
⾃定义命名空间引⼊
import * as symbolName from “filename”
全局引入案例
hello.sol 先进行部署
pragma solidity ^0.4.21;
contract Test{
string str1;
function setValue (string para) public {
str1 = para;
}
function getValue() constant public returns (string){
return str1;
}
}
importTest.sol 再进行部署 参数填写之前合约的地址,”str” 会修改第一个合约的数值
pragma solidity ^0.4.20;
import "./hello.sol";
contract ImportTest {
function setString (Test test, string str) public {
test.setValue(str);
}
}
合约的结构(基本组成元素)
每个合约中可包含状态变量(State Variables),函数(Functions),函数修饰符(Function Modifiers),事件
(Events),结构类型(Structs Types)和枚举类型(Enum Types)
合约类似于⾯向对象语⾔中的 类 ,且 ⽀持继承
状态变量(State Variables)
变量值会永久存储在合约的存储空间
pragma solidity ^0.4.0;
// simple store example
contract simpleStorage{
uint valueStore; //state variable
}
函数(Functions)
智能合约中的⼀个可执⾏单元
pragma solidity ^0.4.0;
contract simpleMath{
//Simple add function,try a divide action?
function add(uint x, uint y) returns (uint z){
z = x + y;
}
}
上述代码实现了⼀个简单的加法函数。合约函数的调⽤分为 内部调⽤internal 和 外部调⽤external ,后续函
数章节会详细介绍。
函数修饰符(Function Modifiers)
pragma solidity ^0.4.22;
contract Purchase {
address public seller;
modifier onlySeller() { // Modifier
require(
msg.sender == seller,
"Only seller can call this."
);
_;
}
function abort() public onlySeller { // Modifier usage
// ...
}
}
事件(Events)
事件是以太坊虚拟机(EVM)⽇志基础设施提供的⼀个便利接⼝,⽤于⽇志输出,便于跟踪调试
pragma solidity ^0.4.21;
contract SimpleAuction {
event HighestBidIncreased(address bidder, uint amount); // Event
function bid() public payable {
// ...
emit HighestBidIncreased(msg.sender, msg.value); // Triggering event
}
}
———————————————————————————————————————-
002.数据类型
值传递与引用传递
值传递 memory(Value Type) 引用传递 storage(Refernce Type) 将改变变量的值 类似于& 红色部分如果改为memory(默认)将不改变_name的值 如果改为 storage 引用传递 函数类型应改为private 在函数内部进行调取
pragma solidity ^0.4.20;
contract Student{
string _name = "lily";
uint _num;
function execute() public{
changeName(_name);
}
function changeName(string storage name) private{
_num = 10;
bytes(name)[0] = "L";
}
function getName() constant public returns (string) {
return _name;
}
function getNum() constant public returns (uint){
return _num;
}
}
//memory(Value Type) storage(Refernce Type)
003.布尔类型
pragma solidity ^0.4.20;
contract BoolTest {
uint v1 = 10;
uint v2 = 20;
string s1 = "hello";
string s2 = "world";
boo flag1 = true;
bool flag2 = false;
function f1() constant public returns (bool) {
return !flag1;
}
function f2() constant public returns (bool) {
return flag1 && flag2;
}
function f3() constant public returns (bool) {
return flag1 || flag2;
}
function f4() constant public returns (bool) {
return v1 == v2;
}
function f5() constant public returns (bool) {
return v1 != v2;
}
}
004.整型
pragma solidity ^0.4.21;
contract IntegerTest {
/*
//&,|,^(异或),~(位取反)
10 uint8 1010
4 uint8 0100
& 0000 0
| 1110 14
^ 1110 14
~ 3 0000,0011 1100 12
1111,1100
1111,1111 - 3
2^8 -1 -3 = 256 -4 = 252
*/
uint8 a = 10;
uint8 b = 4;
uint8 c = 3;
function f1() constant public returns (bool) {
return a & b == 0;
}
function f2() constant public returns (bool) {
return a | b == 14;
}
function f3() constant public returns (bool) {
return a ^ b == 14;
}
function f4() constant public returns (bool) {
return (~c) == 252;
}
function f5() constant public returns (uint8){
return ~c;
}
}
地址(Address)
以太坊地址的⻓度,⼤⼩ 20个字节 , 160位 ,所以可以⽤⼀个 uint160 编码。地址是所有合约的基础,所有
的合约都会继承地址对象,也可以随时将⼀个地址串,得到对应的代码进⾏调⽤。
⽀持的运算符:
描述 符号
⽐较运算符 <=,<,==,!=,>=,>
地址类型的成员:
属性: balance
⽅法: send() , transfer() , call() , delegatecall() , callcode()
地址与整型转换
pragma solidity ^0.4.20;
contract AddressTest {
address _add1 = 0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db;
address _add2 = 0x583031d1113ad414f02576bd6afabfb302140225;
//加public会有一个匿名函数方便展示
uint160 public _num = 0;
address public _add;
function address2uint160() public returns(uint160){
_num = uint160(_add1);
return _num;
}
function uint160ToAddress() public returns(address){
_add = address(_num);
return _add;
}
function comAddress() constant public returns(bool){
return _add1 == _add2;
}
function isGreater() constant public returns(bool){
return _add1 > _add2;
}
}
balance余额
pragma solidity ^0.4.20;
contract addressBalance {
function getBalance(address addr) constant public returns (uint){
return addr.balance;
}
}
sender消息发起人
pragma solidity ^0.4.20;
contract senderTest {
address public _owner; //存储合约创建者地址
// 构造函数
function senderTest() public {
_owner = msg.sender; // 是合约创建者地址 只执行一次
}
function getOwnerBalance() public returns (uint256){
return msg.sender.balance; // 每一个sender发送者的钱包地址 地址会变动
}
function getInvoker() constant public returns (address){
return msg.sender; // 每一个sender发送者的余额 地址会变动
}
}
this获取合约信息及地址
表示智能合约的地址的变量: this指针
如果只是想返回当前合约账户的余额,可以使⽤ this 指针, this 表示合约⾃身的地址
对于合约来说,地址代表的就是合约本身,合约对象默认继承⾃地址对象,所以内部有地址的属性。
注:this和msg.sender不⼀样, this代表的是合约本身的地址,msg.sendr返回的是请求合约的账户的地址
pragma solidity ^0.4.20;
contract addressThis {
uint _money;
// 构造函数
function addressThis() payable public { //payable 表示这个合约可以接受钱了
_money = msg.value; //合约部署时value字段 给的金额
}
function getThis() constant public returns (address){
return this; //返回合约本身
}
function getBalance () constant public returns (uint256){
return this.balance; //合约余额
}
function getMoney() constant public returns(uint){
return _money;
}
function getMsgSender() constant public returns (address){
return msg.sender; //请求发送者地址 变化的
}
}
转账transfer介绍
pragma solidity ^0.4.20;
//在测试网测试通过合约进行转账
//注意value字段在部署合约的时候不要写 当合约部署完成后 调取转账函数时再填写value字段
//acc2 : 8.9 0x794C3fF2068455dbeb5D5D039347d221d10C905f
//Alice : 12.9 0xa446742e4845CFbA8EC7d93226eE6ceB6C7C91ab
contract TransferTest {
address AliceAddress = 0xa446742e4845CFbA8EC7d93226eE6ceB6C7C91ab;
function transfer() payable { //为了避免误操作 转账函数必须含有payable关键字才可以
AliceAddress.transfer(msg.value);
}
}
转账send()函数
这两个⽅法都是⽤来发送货币的,使⽤的参数也都⼀致。不同之处在于:合约运⾏出错或由于gas耗尽⽽停
⽌时, transfer() ⽅法会抛出异常,⽽ send() ⽅法相对于 transfer() ⽽⾔更底层,它不抛异常,⽽是会
返回false。
注:send() 执⾏有⼀些⻛险:如果调⽤栈的深度超过1024或gas耗光,交易都会失败。因此,为了保证安
全,必须检查send的返回值,如果交易失败,会回退以太币。如果⽤transfer会更好。
pragma solidity ^0.4.20;
//acc8 : 6.9 0xa7eEEAC067530608Ed24E76c47A57d1b35702cce
//Alice : 11.9 0xa446742e4845CFbA8EC7d93226eE6ceB6C7C91ab
//注意value字段在部署合约的时候不要写 当合约部署完成后 调取转账函数时再填写value字段
contract TranferTest {
address AliceAddress = 0xa446742e4845CFbA8EC7d93226eE6ceB6C7C91ab;
bool res;
function transfer() payable public returns (bool){
//send()方法转账更底层与transfer用法一样 但是send方法使用处理不当会有风险
res = AliceAddress.send(msg.value);
return res;
}
}
006.枚举类型
枚举类型是在Solidity中的⼀种⽤户⾃定义类型。他可以显示的转换与整数进⾏转换,但不能进⾏隐式转
换。显示的转换会在运⾏时检查数值范围,如果不匹配,将会引起异常。枚举类型应⾄少有⼀名成员,枚举
元素默认为uint8,当元素数量⾜够多时,会⾃动变为uint16,第⼀个元素默认为0,使⽤超出范围的数值时
会报错。
pragma solidity ^0.4.0;
contract enumTest {
enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill } //uint8 顺序 0 1 2 3
ActionChoices _choice;
ActionChoices defaultChoice = ActionChoices.GoStraight;
//设置 0 1 2 3
function setGoStraight(ActionChoices choice) public {
_choice = choice;
}
//读取 0 1 2 3 数字
function getChoice() constant public returns (ActionChoices) {
return _choice;
}
//返回2
function getDefaultChoice() constant public returns (uint) {
return uint(defaultChoice);
}
//输入0返回true
function isGoLeft(ActionChoices choice) constant public returns (bool){
if (choice == ActionChoices.GoLeft){
return true;
}
return false;
}
}
007.函数类型 内部外部类型函数
pragma solidity ^0.4.5;
contract FuntionTest{
uint public v1;
uint public v2;
/* uint public v1 就类似于下面这个函数 在部署时可以查看变量数值
function v1() constant returns (uint) {
return v1;
}
*/
// 只能在合约内部调取 外部合约无法调用
function internalFunc() internal{
v1 = 10;
}
function externalFunc() external returns (uint res){
v2 = 20;
return v2;
}
function resetV2() public {
v2 = 0;
}
function callFunc() {
//直接使用内部的方式调用
internalFunc(); //<--- 合约内部直接调用,正确
//不能在内部调用一个外部函数,会报编译错误。
//Error: Undeclared identifier.
//externalFunc(); //<--- 外部合约可以调用
//不能通过`external`的方式调用一个`internal`
//Member "internalFunc" not found or not visible after argument-dependent lookup in contract FuntionTest
//this.internalFunc(); //<--- this相当于外部调用
//使用`this`以`external`的方式调用一个外部函数
this.externalFunc();
}
}
// 继承 FuntionTest 所有方法 可以用internal 不可以用external
contract Son is FuntionTest {
function callInternalFunc() public{
internalFunc();
//externalFunc(); 无法调取外部函数
}
}
contract FunctionTest1{
uint public v3;
function externalCall(FuntionTest ft){ //FuntionTest ft 参数 输入第一个合约部署的合约地址
//调用另一个合约的外部函数
v3 = ft.externalFunc(); //得到20 将改变v3结果
//不能调用另一个合约的内部函数
//Error: Member "internalFunc" not found or not visible after argument-dependent lookup in contract FuntionTest
//ft.internalFunc();
}
function resetV3() public {
v3 = 0;
}
}
008.数组
1. 定⻓数组bytes1 … bytes32
字节不可修改
⻓度不可修改
⽀持 length (只读)
⽀持下标索引
pragma solidity ^0.4.0;
contract C {
bytes10 public b = "helloworld";
uint public len;
function f() public {
len = b.length;
//b.length = 10; //ERROR,⻓度为只读,不可修改
}
bytes1 public c = b[0];
//b = "HELLO";ERROR,定义之后不可修改
}
byte[n]
引⽤类型
⽀持下标
⻓度可不变
可以修改
pragma solidity ^0.4.4;
contract C {
bytes9 bb = 0xabcde;
byte[9] aa = [byte(0x6c),0x69,0x79,0x75,0x65,0x63,0x68,0x75,0x6e];
byte[8] cc = [byte("a"), "b"];
uint len = aa.length;
function test() public{
aa[0] = 0xa1;
//bb[0] = 0xa1;
}
}
不定⻓数组
内容和⻓度均可修改,包括以下三种: 类型[⻓度] 、 bytes 、 string
第⼀种:类型[⻓度]
动态数组
可以修改
可以改变⻓度(storage)
⽀持 length 、 push ⽅法
可使⽤new关键字创建⼀个memory的数组。与stroage数组不同的是,你不能通过.length的⻓度来修改数组
⼤⼩属性。我们来看看下⾯的例⼦:
pragma solidity ^0.4.0;
contract C {
function f() {
//创建⼀个memory的数组
uint[] memory a = new uint[](7);
//不能修改⻓度
//Error: Expression has to be an lvalue.
//a.length = 100;
for (uint i = 0; i< a.length; i++){
a[i] = i;
}
//a.push(10);
}
//storage
uint[] public b;
function g(){
b = new uint[](7);
//可以修改storage的数组
b.length = 10;
b[9] = 100;
b.push(101);
}
}
直接创建
* 内容可变
* ⻓度不可变
* ⽀持length⽅法,不⽀持push
pragma solidity ^0.4.5;
contract test {
uint [10] value = [1,2,3,4,5];
uint public sum;
function getSum(){
sum = 0;
for (uint i = 0; i < value.length; i++){
sum += value[i];
}
}
function changeValue(){
value[0] = 2;
}
}
第⼆种:bytes 动态字节数组
引⽤类型
⽀持 下标索引
⽀持 length 、 push ⽅法
可以修改
pragma solidity ^0.4.4;
contract C {
bytes public _name = new bytes(1);
bytes public _name2;
function setLength(uint length) {
_name.length = length;
}
function getLength() constant returns (uint) {
return _name.length;
}
function setName(bytes name){
_name = name;
}
function changeName(bytes1 name){
_name[0] = name;
}
function setInside(){
_name = "helloWorld";
_name2 = "HELLOWORLD2";
}
}
第三种:string
动态尺⼨的UTF-8编码字符串,是特殊的可变字节数组
引⽤类型
不⽀持下标索引
不⽀持 length 、 push ⽅法
可以修改(需通过bytes转换)
注:bytes和string可以⾃由转换
string转换成bytes
pragma solidity ^0.4.4;
contract C {
string public _name = "lily";
function nameBytes() constant returns (bytes) {
return bytes(_name);
}
function nameLength() constant returns (uint) {
return bytes(_name).length;
}
function changeName() public {
bytes(_name)[0] = "L";
//s2[0] = "H"; //ERROR,不⽀持下标索引
}
function changeLength() {
bytes(_name).length = 15;
bytes(_name)[14] = "x";
}
}
pragma solidity ^0.4.20;
contract Test {
bytes10 b10 = 0x68656c6c6f776f726c64; //helloworld 文本转为16进制
bytes public bs10 = new bytes(b10.length);
//1. 固定字节数组转动态字节数组
function fixedBytesToBytes() public{
for (uint i = 0; i< b10.length; i++){
bs10[i] = b10[i];
}
}
//2、字符串转为bytes
string greeting = "helloworld";
bytes public b1;
function StringToBytes() public {
b1 = bytes(greeting);
}
//3.string转动态字节数组
string public str3;
function BytesToString() public {
str3 = string(bs10);
}
}
009.结构体
pragma solidity ^0.4.5;
contract Test {
struct Student {
string name;
uint age;
uint score;
string sex;
}
Student public stu1 = Student("lily",18,90,"girl");
Student public stu2 = Student({name:"jim",age:20,score:80,sex:"boy"});
Student[] public Students;
function assign() public {
Students.push(stu1);
Students.push(stu2);
stu1.name = "Lily";
}
}
010.映射/字典 (mapping)
键的类型允许除映射外的所有类型,如数组,合约,枚举,结构体。值的类型⽆限制。
creates a namespace in which all possible keys exist, and values are initialized to 0/false.
所以⽆法判断⼀个mapping中是否包含某个key,因为它认为每⼀个都存在,只不过是0或false
映射可以被视作为⼀个哈希表,其中所有可能的键已被虚拟化的创建,被映射到⼀个默认值(⼆进制表示的
零)。在映射表中,我们并不存储键的数据,仅仅存储它的 keccak256 哈希值,⽤来查找值时使⽤。
映射类型,仅能⽤来定义状态变量,或者是在内部函数中作为storage类型的引⽤
pragma solidity ^0.4.20;
contract test {
// id -> name
mapping(uint => string) id_names;
constructor() public {
id_names[0x001] = "lily";
id_names[0x002] = "Jim";
id_names[0x002] = "mike";
}
function getNameById(uint id) constant public returns (string){
string storage name = id_names[id];
return name;
}
}
11.⾃动推导类型
为了⽅便,并不总是需要明确指定⼀个变量的类型,编译器会通过第⼀个向这个对象赋予的值的类型来进⾏
推断
uint24 x = 0x123; var y = x;
函数的参数,包括返回参数,不可以使⽤var这种不指定类型的⽅式。
需要特别注意的是,由于类型推断是根据第⼀个变量进⾏的赋值。所以下⾯的代码将是⼀个⽆限循环,,因
为⼀个uint8的i的将⼩于2000。
for (var i = 0; i < 2000; i++)
{
//⽆限循环
}
演示
pragma solidity ^0.4.4;
contract Test{
function a() returns (uint){
uint count = 0;
for (var i = 0; i < 257; i++) { count++; if(count >= 258){
break;
}
}
return count;
}
}
12.全局函数
将如下代码使用remix在metamask进行部署,并且与合约进行交互调取 tt()方法进行查看 如果遇到GAS不足错误提示 增大GAS费用 (增大“燃料限制”数字)
pragma solidity ^0.4.21;
contract Test {
bytes32 public _blockhash;
address public coinbase;
uint public difficulty;
uint public gaslimit;
uint public blockNum;
uint public timestamp;
bytes public calldata;
uint public gas;
address public sender;
bytes4 public sig;
uint public msgValue;
uint public _now;
uint public gasPrice;
address public txOrigin;
function tt () payable public {
//给定区块号的哈希值,只支持最近256个区块,且不包含当前区块
_blockhash = blockhash(block.number - 1);
coinbase = block.coinbase ;//当前块矿工的地址。
difficulty = block.difficulty;//当前块的难度。
gaslimit = block.gaslimit;// (uint)当前块的gaslimit。
blockNum = block.number;// (uint)当前区块的块号。
timestamp = block.timestamp;// (uint)当前块的时间戳。
calldata = msg.data;// (bytes)完整的调用数据(calldata)。
gas = gasleft();// (uint)当前还剩的gas。
sender = msg.sender; // (address)当前调用发起人的地址。
sig = msg.sig;// (bytes4)调用数据的前四个字节(函数标识符)。
msgValue = msg.value;// (uint)这个消息所附带的货币量,单位为wei。
_now = now;// (uint)当前块的时间戳,等同于block.timestamp
gasPrice = tx.gasprice;// (uint) 交易的gas价格。
txOrigin = tx.origin;// (address)交易的发送者(完整的调用链)
}
}
13.货币单位与时间单位介绍
pragma solidity ^0.4.0;
contract EthUnit{
uint a = 1 ether;
uint b = 10 ** 18 wei;
uint c = 1000 finney;
uint d = 1000000 szabo;
function f1() constant public returns (bool){
return a == b;
}
function f2() constant public returns (bool){
return a == c;
}
function f3() constant public returns (bool){
return a == d;
}
function f4() pure public returns (bool){
return 1 ether == 100 wei;
}
}
contract TimeUnit{
function f1() pure public returns (bool) {
return 1 == 1 seconds;
}
function f2() pure public returns (bool) {
return 1 minutes == 60 seconds;
}
function f3() pure public returns (bool) {
return 1 hours == 60 minutes;
}
function f4() pure public returns (bool) {
return 1 days == 24 hours;
}
function f5() pure public returns (bool) {
return 1 weeks == 7 days;
}
function f6() pure public returns (bool) {
return 1 years == 365 days;
}
}
constant、view、pure介绍
pragma solidity ^0.4.20;
contract Test {
//一、constant介绍
uint public v1 = 10;
uint constant v2 = 10;
string str1 = "hello!";
string constant str2 = "test!";
function f1() public {
v1 = 20;
//v2 = 30; //constant修饰的值类型无法被修改
str1 = "Hello!";
// str2 = "Test!";
}
struct Person {
string name;
uint age;
}
//错误的 -> Person constant p1; //constant仅可以修饰值类型,无法修饰引用类型(string除外)
function f2() constant public{
v1 = 20; //constant 修饰的函数内,如果修改了状态变量,那么状态变量的值是无法改变的,小心!!
}
//二、view介绍
// 1. view只可以修饰函数
// 2. 它表明该函数内尽可以对storage类型的变量进行读取,无法修改
//三、pure介绍
//pure
// 1. pure只可以修饰函数
// 2. 它表明该函数内,无法读写状态变量
function f3() pure public returns(uint){
//return v1; 读写都错误
}
}
15.错误处理
传统⽅法:采⽤ throw 和 if … throw 模式(已过时)
例如合约中有⼀些功能,只能被授权为 拥有者 的地址才能调⽤。
新⽅法:
新函数 require() , assert() , revert() 提供了同样功能,⽽且上下⽂更加⼲净。
下⾯的代码:
if(msg.sender != owner) { throw; }
等价于:
if(msg.sender != owner) { revert(); }
assert(msg.sender == owner);
require(msg.sender == owner);
注意在 assert() 和 require() 例⼦中的条件声明,是 if 例⼦中条件块取反,也就是⽤ == 代替了 != 。
pragma solidity ^0.4.21;
contract HasAnOwner {
address public owner;
uint public a ;
constructor() public {
owner = msg.sender;
}
function useSuperPowers() public {
require(msg.sender == owner);
/* 等同于 如果发送者不是创建合约者会抛出异常
if (msg.sender != owner){
throw;
}
*/
a = 10;
// do something only the owner should be allowed to do
}
}
16.delete介绍
delete操作符可以⽤于任何变量,将其设置成默认值
如果对动态数组使⽤delete,则删除所有元素,其⻓度变为0
如果对静态数组使⽤delete,则重置所有索引的值
如果对map类型使⽤delete,什么都不会发⽣
指定键删除:但如果对map类型中的⼀个键使⽤delete,则会删除与该键相关的值
pragma solidity ^0.4.21;
contract deleteTest {
string public str1 = "hello";
//delete操作符可以用于任何变量(mapping除外),将其设置成默认值
function delStr() public{
delete str1;
}
function setStr() public {
str1 = "HELLO";
}
//静态数组,动态数组
uint[10] public staticArray = [4,2,4,5,6,7,8];
uint[] public dynamicArray = new uint[](10);
function intDynamicArray () public {
for (uint i = 0; i< 10; i++) { //dynamicArray[i] = i; dynamicArray.push(i); } } //如果对静态数组使用delete,则重置所有索引的值 function delStaticArray() public { delete staticArray; } //如果对动态数组使用delete,则删除所有元素,其长度变为0 function delDynamicArray() public { delete dynamicArray; } function getArrayLength() view public returns (uint, uint){ return (staticArray.length, dynamicArray.length); } mapping(uint => bool) public map1;
function initMap() public {
map1[1] = true;
map1[2] = true;
map1[3] = false;
//delete map1;
}
function deleMapByKey(uint key) public {
delete map1[key];
}
//delete map1;
struct Person {
string name;
mapping(string => uint) nameScore;
}
Person public p1;
function initP1() public {
p1.name = "duke";
p1.nameScore["duke"] = 80;
}
function returnP1() view public returns (string, uint) {
return (p1.name, p1.nameScore["duke"]);
}
function deleteP1() public {
delete p1;
}
}
17.modifier修饰器介绍
修改器(Modifiers)可以⽤来轻易的改变⼀个函数的⾏为。⽐如⽤于在函数执⾏前检查某种前置条件。修改器
是⼀种合约属性,可被继承,同时还可被派⽣的合约重写(override)。下⾯我们来看⼀段示例代码:
本代码在运行useSuperPowers()函数时先运行ownerOnly() 修饰器条件校验 校验成功后 _;代码回换为 a = 10; 要执行的代码 直接执行 否则将失败
pragma solidity ^0.4.21;
contract HasAnOwner {
address public owner;
uint public a ;
constructor() public {
owner = msg.sender;
}
modifier ownerOnly(address addr) {
require(addr == owner);
//代码修饰器所修饰函数的代码
_;
}
function useSuperPowers() ownerOnly(msg.sender) public {
a = 10;
// do something only the owner should be allowed to do
}
}
继承
Solidity通过复制包括多态的代码来⽀持多重继承。
所有函数调⽤是虚拟(virtual)的,这意味着最远的派⽣⽅式会被调⽤,除⾮明确指定了合约。
当⼀个合约从多个其它合约那⾥继承,在区块链上仅会创建⼀个合约,在⽗合约⾥的代码会复制来形成继承
合约。
表达式和控制结构不⽀持switch和goto,⽀持if,else,while,do,for,break,continue,return,?:

