solidity是开发智能合约的常用语言,下面是一些关于solidity的基础知识。

目录

注释

导入其他源文

变量类型

全局变量

货币单位

常用修饰符

修改器modifier

库的使用

异常

源代码结构

源文件中可以包含任意多个 合约定义 、导入源文件指令 、 版本标识 指令、 结构体 、 枚举 和 函数 定义。

SPDX 版权许可标识(The Software Package Data Exchange):这行就是在指明你写的智能合约采用什么样的对外开放标准,该标准规定了别人是否拥有商业开发,学习使用等权利。

常见开源:

// SPDX-License-Identifier: MIT

// SPDX-License-Identifier: GPL-3.0

私有或者无授权:

// SPDX-License-Identifier: UNLICENSED

版本标识

pragma solidity ^0.7.0;或者 pragma solidity >=0.7.0 <0.9.0;

ABI Coder Pragma

Solidity 0.7.4 之前:pragma experimental ABIEncoderV2

Solidity 0.7.4 之后:pragma abicoder v2

注释

natspec注释
单行://
多行:/**/

导入其他源文

导入其它源文件,用import

代码:
1,import “filename”
2, import * as name from “”
3.import “” as name;
4,import {symbol1 as a,symbol2} from “”;

合约结构

每个合约都可以包含状态变量,函数,函数修饰符,事件,日志,结构类型,枚举类型的声明。此外,合约可以继承自其他合约。

状态变量

状态变量是其值永久存储在合约存储中的变量

变量类型

(开发小注意:1.有的版本字符串赋值不能直接用中文字符串,加 unicode修饰值类型;
2.超界问题,SafeMath库进行数据操作
)

函数

函数是代码的可执行单元。函数通常在合约内定义,但也可以在合约外定义。

函数修饰符

函数修饰符可用于以声明的方式修改函数的语义(请参阅合约部分中的函数修饰符)。

常用修饰符

1.public和private:public表示公有都可以使用,private表示只能被这个合约使用

2.internal和external:  internal跟private比较类似,但是internal修饰的可以被继承的合约使用

3.external和public类似,但是不能被该合约使用

4.pure和view:view表示这个函数不会修改和保存任何东西,pure表示只跟输出只跟输入有关,不但不在区块链写数据而且不会用里面的数据,只会用输入的数据

5.payable修饰符(可支付):

  • payable函数,是可以接受以太币的特殊函数,
  • 任何函数,只要修饰为payable,那么就可以在调用这个方法的时候,对value字段赋值,然后将价值value的钱转给合约。
  • 若这个函数没有指定payable,但是对value赋值了,那么本次调用会报错。

修改器modifier

使用修改器modifier可以轻松改变函数的行为。 例如,它们可以在执行函数之前自动检查某个条件。修改器modifier是合约的可继承属性,并可能被派生合约覆盖 。

调用函数修饰符常常用在函数的后面跟public和private等一个位置用来判断是否执行该函数

    函数修饰符通常使用下划线结尾

    如:modifier olderThan(uint _age,uint _userId){

      require(age[_userID]>= _age);

      _; //以_;结尾表示函数正常返回继续执行之前的功能

  }

事件

事件是与 EVM 日志记录工具的便利接口。

日志

为故障情况定义描述性名称和数据。

结构类型

结构是自定义定义的类型,可以对多个变量进行分组

枚举类型

枚举可用于创建具有一组有限“常量值”的自定义类型

全局变量

  • abi.encode(...) returns (bytes):对给定的参数进行ABI编码。
  • abi.encodePacked(...) returns (bytes): Performes packed encoding of the given arguments
  • abi.encodeWithSelector(bytes4 selector, ...) returns (bytes)::对给定的参数进行ABI编码——从第二个预置给定的四字节选择器开始
  • abi.encodeWithSignature(string signature, ...) returns (bytes):相当于abi.encodeWithSelector(bytes4(keccak256(signature), ...)
  • block.blockhash(uint blockNumber) returns (bytes32): 给定的块的hash值, 只有最近工作的256个块的hash值—— 在 0.4.22 后请使用blockhash(uint blockNumber).
  • block.coinbase(address): 当前块的矿工的地址
  • block.difficulty(uint): 当前块的难度
  • block.gaslimit(uint): 当前块的gaslimit
  • block.number(uint):当前块的数量
  • block.timestamp(uint):当前块的时间戳
  • gasleft() returns (uint256): 剩余 gas
  • msg.data(bytes): 完整的calldata
  • msg.gas(uint): 剩余 gas – 0.4.21后请使用gasleft()
  • msg.sender(address): 消息的发送者(当前调用)
  • msg.value(uint): 和消息一起发送的wei的数量
  • now(uint): 当前块的时间戳(block.timestamp的别名)
  • tx.gasprice(uint):交易的gas价格
  • tx.origin(address):交易的发送者(全调用链)
  • assert(bool condition): abort execution and revert state changes if condition isfalse(用于内部错误)
  • require(bool condition): abort execution and revert state changes if condition isfalse(用于输入错误或外部组件的错误)
  • require(bool condition, string message): abort execution and revert state changes if condition isfalse(用于输入错误或外部组件的错误). 并提供错误信息.
  • revert(): 中止执行并还原状态更改
  • revert(string message):中止执行并还原状态更改,提供解释字符串
  • blockhash(uint blockNumber) returns (bytes32): : 给定的块的hash值, 只有最近工作的256个块的hash值
  • keccak256(...) returns (bytes32):计算(紧凑排列的)参数的 Ethereum-SHA3 hash值
  • sha3(...) returns (bytes32): an alias tokeccak256
  • sha256(...) returns (bytes32): 计算(紧凑排列的)参数的SHA256 hash值
  • ripemd160(...) returns (bytes20):计算 256个(紧凑排列的)参数的RIPEMD
  • ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address): 椭圆曲线签名公钥恢复,错误时返回0
  • addmod(uint x, uint y, uint k) returns (uint): compute(x + y) % kwhere the addition is performed with arbitrary precision and does not wrap around at2**256. Assert thatk != 0starting from version 0.5.0.
  • mulmod(uint x, uint y, uint k) returns (uint): compute(x * y) % kwhere the multiplication is performed with arbitrary precision and does not wrap around at2**256. Assert thatk != 0starting from version 0.5.0.
  • this(current contract’s type): 当前合约,在地址上显式转换
  • super: 在层次关系上一层的合约
  • selfdestruct(address recipient): 销毁当前的合约,将其资金发送到指定address
  • suicide(address recipient): a deprecated alias toselfdestruct
  • .balance(uint256): address地址中的账户余额(以wei为单位)
  • .send(uint256 amount) returns (bool): 将一定量wei发送给address地址,若失败返回false
  • .transfer(uint256 amount): 将一定量wei发送给address地址,若失败抛出异常。

货币单位

以太 ETH
最小单位wei
常用单位:finney,szabo,ether,wei

库的使用

关键词library using for

using for 扩展类型
A是库library
using A for B 把库函数(从库A)关联到类型B
A库有函数add(B b), 则可使用b.add()

ABI:以太坊调用合约时的接口说明,即定义操作函数签名,参数编码,返回编码。
映射:引用类型,存储键,值对
结构体:结构体是可以将几个变量分组的自定义类型
枚举类型 enum:枚举可用来创建由一定数量的“常量值”构成的自定义类型

异常

1.条件检查
Solidity提供了assert和require来进行
* require: require函数来检查输入变量或合约状态变量是否满足条件以及验证调用外部合约返回值。可以有返回值require(condition, ‘Something bad happened’);
* assert: assert函数通常用来检查(测试)内部错误

注:同样作为判断一个条件是否满足的函数,require会退回剩下的gas,而assert会消耗所有的gas。

2.触发异常
提供了revert,throw来触发异常:
* throw:关键字抛出异常(从0.4.13版本,throw关键字已被弃用,将来会被淘汰。)回滚所有状态改变,返回”无效操作代码错误”,而且消耗掉剩下的gas
* revert:函数可以用来标记错误并回退当前调用,允许返回一个数值,将剩余gas返还调用者

传统处理异常的方式if…throw模式
即 if(msg.sender != owner) { throw; }
等价于:
* if(msg.sender != owner) { revert(); }//如果不等则异常
* assert(msg.sender == owner);//校验是否等于
* require(msg.sender == owner);

3.如何选择
require()函数用于:
(1).确认有效条件,例如输入,
(2).确认合约声明变量是一致的
(3).从调用到外部合约返回有效值

revert()函数用于 :
处理与 require() 同样的类型,但是需要更复杂处理逻辑的场景
如果有复杂的 if/else 逻辑流,那么应该考虑使用 revert() 函数而不是require()。

assert()函数用于:预防本不该发生的事情,如果发生就意味着合约中存在需要修复的bug(比如assert(1 > 2))。一般地,尽量少使用 assert 调用,一般assert 应该在函数结尾处使用

事件与日志

事件event是以太坊提供的基本功能,用于将数据记录成日志保存到区块链上,用户可以自定义需要记录的数据,以及topic和索引;日志是指事件保存在区块链上的数据。事件强调操作行为,日志强调存储内容,两者是完全不同的概念。

使用关键字event来定义事件

参考资料:Solidity 最新(0.8.0)中文文档 — Solidity中文文档 — 登链社区

Solidity开发——01智能合约与Solidity语言简介_哔哩哔哩_bilibili