函数
基础概念
函数定义
- 函数定义语法
function function_name(<parameter list>) <visibility> <state mutability>
[ returns(<return type>)]{
// 语句
}
- 可见性(visibility)
- private(私有):函数只能在所定义的智能合约内部调用
- internal(内部):可以在所定义合约内部调用该函数,也可以从继承合约中调用该函数
- external(外部):只能从合约外部调用。如果要从合约中调用它,则必须使用this。
- public(公开):可以从任何地方调用
- 状态可变性(mutability)
- view:用 view 声明的函数只能读取状态,而不能修改状态
- pure:用 pure 声明的函数既不能读取也不能修改状态
- payable:用 payable 声明的函数可以接受发送给合约的以太币,如果未指定,该函数将自动拒绝所有发送给它的以太币。
- 示例
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract SoldityTest{ constructor(){} function getResult() public view returns(uint){ uint a = 1; // 局部变量 uint b = 2; uint result = a+b; return result; } }
函数调用
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract SolidityTest{
constructor(){}
function getResult() public pure returns(string memory){
return integerToString(3); // 调用函数 integerToString
}
// integerToString 函数定义
function integerToString(uint _i)internal pure
returns(string memory){
if(_i == 0){
return "0";
}
uint j = _i;
uint len;
while(j!=0){
len++;
j/=10;
}
bytes memory bstr = new bytes(len);
uint k = len - 1;
while(_i != 0){
bstr[k--] = bytes1(uint8(48 + _i % 10));
_i /= 10;
}
return string(bstr);
}
}
return 语句
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Test{
function getResult() public view returns(uint product,uint sum){
uint a = 1; // 局部变量
uint b = 2;
product = a*b; // 使用返回参数返回值
sum = a+b; // 使用返回参数返回值
// 也可以使用 return 返回多个值
// return(a*b,a+b);
}
}
函数返回值
- 函数返回值可以使用名字,也可以使用匿名方式
- 可以通过名字赋值,也可以使用 return 返回
- 支持多个返回值
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract funreturn{
// 返回值可以有名字
function returnTest() public view returns(uint mul){
uint a = 10;
return a;
}
// 可以直接为返回值赋值
function returnTest2()public view returns(uint mul){
mul = 10;
}
// 当给返回值赋值后,并且有多个 return,以最后的 return 为主
function returnTest3() public view returns(uint mul){
uint a = 10;
mul = 100;
return a;
}
// 返回常量,自动匹配
function returnTest4() public view returns(uint mul){
uint a = 10;
mul = 100;
return 1;
}
// 函数可以有多个返回值,多返回值赋值
function returnTest5(uint a,uint b)public view returns(uint add,uint mul){
add = a+b;
mul = a*b;
}
// 函数可以有多返回值,返回 return(param list)
function returnTest6(uint a,uint b)public view returns(uint add,uint mul){
return(a+b,a*b);
}
// 交换变量的值
function returnTest7(uint a,uint b)public view returns(uint a1,uint b1){
return(b,a);
}
}
- 函数多返回值的调用方法
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract funreturn{
// 定义多返回值函数
function returnFunc() private pure returns(uint a,uint b){
return(1,2);
}
// 调用多返回值函数
function callFunc1() public pure returns(uint,uint){
(uint r1,uint r2) = returnFunc();
return (r1,r2);
}
// 调用多返回值函数,先定义变量
function callFunc2() public pure returns(uint,uint){
uint r1;
uint r2;
(r1,r2) = returnFunc();
return(r1,r2);
}
// 调用多返回值函数,但只取第一个返回值
function callFunc3() public pure returns(uint,uint){
(uint r1,) = returnFunc();
return(r1,100);
}
}
pure 函数
- pure 函数(纯函数),指函数不会读取或修改状态,即不会操作链上数据
- 如果函数中存在以下语句,则被视为读取状态,编译器将抛出警告
- 读取状态变量
- 访问 address(this).balance 或 <address>.balance
- 访问任何区块、交易、msg等reshuffle变量(msg.sig 与 msg.data 允许读取)
- 调用任何不是纯函数的函数
- 使用包含特定操作码的内联程序集
- 如果发生错误,pure 函数可以使用 revert() 和 require() 函数来还原潜在的状态更改
- 声明为 pure函数,可以在函数声明里,添加 pure 关键字
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Test {
function getResult() public pure returns(uint product, uint sum){
uint a = 1;
uint b = 2;
product = a * b;
sum = a + b;
}
View 函数
- view 函数,也就是视图函数,是指函数只会读取状态,不会修改状态
- 也就是说 view 函数只会读取链上数据,不会修改链上数据
- 如果函数中存在以下语句,则会被视为修改状态,编译器将抛出警告
- 修改状态变量
- 触发事件
- 创建合约
- 使用 selfdestruct
- 发送以太
- 调用任何不是视图函数或纯函数的函数
- 使用底层调用
- 使用包含某些操作码的内联程序集
- 状态变量的 Getter 方法默认是 view 函数
- 声明为 view 视图函数,可以在函数声明里,添加 view 关键字
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract ViewDemo{
uint factor = 2; // 状态变量
function getResult(uint num) public view returns(uint){
uint result = num * factor;
return result;
/* 在这个例子中,函数getResult中,只是读取了状态变量factor的值,而没有修改它的值,所以这个函数是一个view函数。 */
}
}
构造函数
- 构造函数是一个特殊函数,它仅能在合约部署的时候调用一次,之后就不能再次被调用
- 常用来进行状态变量的初始化工作
- 使用 constructor 作为构造函数
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Test{
uint a;
// 不带参数的构造函数
constructor(){
a = 0;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Test{
uint a;
// 带参数的构造函数
constructor(uint _a){
a = _a;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Test {
int public a ;
address public owner;
constructor(int _a) public{
// 将部署者地址存储到owner变量
owner = msg.sender;
// 将参数_a存储到a变量
a = _a;
}
}
函数修改器
- modifier 用于声明一个函数修改器
- 可以将通用的操作提取出来,包装为函数修改器,来提高代码复用性,改善编码效率
- modifier 的作用与 Java Spring 的切面功能很相似,当它作用于一个函数上,可以在函数执行前或后预先执行 modifier 中的逻辑,以增强其功能
- modifier 常用于在函数执行前检查某种前置条件
- modifier 是一种合约属性,可被继承,同时还可被派生的合约重写(override)
- 基本函数修改器
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract ModifierTest{
bool public paused;
uint public counter;
function setPaused(bool _paused)external{
paused = _paused;
}
modifier isNotPaused(){
require(!paused); // 检查前置条件,判断paused是否被设置,如果paused为true那么终止执行
_; // 执行被 isNotPaused 修饰的函数
}
function add() external isNotPaused(){
counter++;
}
}
/* add函数被修改器isNotPaused修饰,所以先执行 require(!paused),检查前置条件,然后再执行add函数的代码。 */
- _ 的作用
- 函数修改器中有一行代码只有下划线 _ ,我们认为下划线 _ 代表了被修饰函数的代码
- 下划线实际上帮我们标记了被 modifier 修饰函数的执行位置
modifier isNotPaused(){
require(!paused); // 检查前置条件,判断paused是否被设置,如果paused为true那么终止执行
_; // 执行被 isNotPaused 修饰的函数
}
/* 下划线 _在 require(!paused) 后面,则被修饰函数 add 在此判断条件之后执行。 */
- 带参数的函数修改器
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract ModifierTest{
bool public paused;
uint public counter;
function setPaused(bool _pasued) external{
paused = _pasued;
}
modifier isNotPaused(uint x){
require(x>10); // 检查前置条件:判断x是否大于10,如果x大于或者等于10,那么终止执行
_; // 执行被 isNotPaused 修饰的函数
}
function add(uint x)external isNotPaused(x){
counter++;
}
}
- 复杂示例
- 函数修改器经典的应用 OpenZeppelin 库中的 Ownable 合约
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/* Ownable 可以判断合约的调用者是否为当前合约的 owner
从而避免其他人随意的调用一些合约的关键操作
同时,owner 可以指定任何其他人为此合约新的 owner
显然,只有当前 owner 才能指定其他人为新的 owner */
contract Ownable{
// 变量 owner 指定此合约的 owner
address public owner;
// 发布时间 - 此合约 owner 已经换人(此逻辑于 modifier 无关,可以忽略)
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
// 构造函数 创建合约自动执行,初始化合约所有人为合约创建者
constructor(){
owner = msg.sender;
}
// 定义一个函数修改器
modifier onlyOwner(){
// 判断此函数调用者是否为 owner
require(msg.sender == owner);
_;
}
// owner 可以用此函数将 owner 所有权转换给其他人,显然此函数只有 owner 才能调用
// 函数末尾加上 onlyOwner 声明,onlyOwner 正是上面定义的 modifier
function transferOwnership(address newOwner) public onlyOwner{
require(newOwner != address(0));
emit OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
}
- 上述合约的 transferOwnership 函数用于 owner 将所有权转让给其他人
- 于是在末尾声明 onlyOwner 修改器,onlyOwner 将在 transferOwnership 执行前,先执行
require(msg.sender == owner);
- 以保证此函数的调用者为 owner,如果不是 owner 则抛出异常
函数重载
- 函数重载,是指同一个作用域内,相同函数名可以定义多个函数
- 这些函数的参数(参数类型或参数数量)必须不一样,仅仅是返回值不一样是不被允许的
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Test{
function getSum(uint a,uint b)public pure returns(uint){
return a+b;
}
function getSum(uint a, uint b,uint c)public pure returns(uint){
return a+b+c;
}
function callSumWithTwoArguments() public pure returns(uint){
return getSum(1, 2);
}
function callSumWithThreeArguments() public pure returns(uint) {
return getSum(1, 2, 3);
}
}
/* 首先单击 callSumWithTwoArguments 按钮,然后单击 callSumWithThreeArguments 按钮查看结果 */
数学函数
- addmod(uint x,uint y,uint k) returns (uint),计算(x+y)%k
- 计算中,以任意精度执行假发,且不限于 2^256 大小
- mulmod(uint x,uint y,uint k) returns (uint),计算(x+y)%k
- 计算中,以任意精度执行惩罚,且不限于 2^256 大小
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Test{
function callAddMod() public pure returns(uint){
return addmod(4, 5, 3);
}
function callMulMod() public pure returns(uint){
return mulmod(4, 5, 3);
}
}
/* 首先单击callAddMod 按钮,然后单击 callMulMod 按钮查看结果 */
加密函数
-
kecckak256 (bytes memory) returns (bytes32) 计算输入的 Keccak-256 散列
-
sha256(bytes memory) returns (bytes32) 计算输入的SHA-256散列。
-
ripemd160(bytes memory) returns (bytes20) 计算输入的RIPEMD-160散列。
-
ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)
-
从椭圆曲线签名中恢复与公钥相关的地址,或在出错时返回零
-
函数参数对应于签名的 ECDSA 值:
-
r- 签名的前32字节
-
s- 签名的第二个32字节
-
v 签名的最后一个字节
-
-
这个方法返回一个地址
-
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Test{
function callKeccak256() public pure returns(bytes32 result){
return keccak256("ABC");
}
}
// 输出:0: bytes32: result 0xe1629b9dda060bb30c7908346f6af189c16773fa148d3366701fbaa35d54f3c8