基于以太坊的智能合约开发Solidity(基础篇)

参考教程:基于以太坊的智能合约开发教程【Solidity】_哔哩哔哩_bilibili

1、第一个程序------Helloworld:

javascript 复制代码
//声明版本号(程序中的版本号要和编译器版本号一致)
pragma solidity ^0.5.17;
//合约
contract HelloWorld
{
    //合约属性变量,也叫状态变量(定义方式:数据类型 变量名 = 数据)
    string myName = "helloworld";  //在solidity中,用单引号包含字符串也是可以的
    //合约中的方法(注意语法顺序,其中此处"view"代表方法只读,不会消耗燃料;"returns"后的是返回值类型)
    function getName() public view returns(string)
    {
        return myName;  //返回值类型要与returns声明的严格相同
    }
    //可以修改属性变量的值,但是会消耗燃料
    function changeName(string _newName) public
    {
        myName = _newName;
    }
    //"pure"代表不能读取也不能改变状态变量
    function pureName(string _name) public pure returns(string)
    {
        return _name;
    }
    /*
    用constant、view、pure修饰function分别表示:
    constant:只能读取不可改变状态变量(就是contract中定义的变量)
    view:只能读取不可改变状态变量,和constant一样
    pure:不能读取也不能改变状态变量
    */
    
}

(1)程序编译完成后,需要在虚拟机上运行,将合约部署好后便可执行刚刚编写的函数。(注意,合约一旦部署,就会永久存在于区块链上,且不可篡改,不过可以销毁)

(2)执行完成后,可以得到以下交易信息,下图所示的就是生成的区块信息:

(3)代码相关:

①属性变量定义方式:数据类型 变量名 = 数据(变量命名规则和c语言相同)

②一般用双引号包含一串字符串,不过在solidity中,用单引号包含字符串也是可以的。

③Solidity值类型:(在Solidity中,一旦一个变量被指定了某个数据类型,如果不经过强制转换,那么它就永远是这个数据类型,在调用函数时如果形参和实参类型不同将会报错

• 布尔类型(bool):可能的取值为字符常量值true或false

• 整型(int/uint):分别表示有符号和无符号的不同位数的整型变量,支持关键字uint8到 uint256(无符号,从8位到256位)以及int8到int256,以8位为步长递增

• 定长浮点型(fixed / ufixed):表示各种大小的有符号和无符号的定长浮点型,在关键字ufixedMxN和fixedMxN中,M表示该类型占用的位数,N表示可用的小数位数

• 地址(address):存储一个 20 字节的值(以太坊地址大小)

• 定长字节数组:关键字有 bytes1,bytes2,bytes3,...,bytes32

• 枚举(enum):一种用户可以定义类型的方法,与C语言类似,默认从0开始递增,一般用来模拟合约的状态

• 函数(function):一种表示函数的类型

④solidity函数的标准形式:

javascript 复制代码
function functionName() {private|internal|external|public} [pure|constant|view|payable] [returns()]

在合约中定义函数要以function开头,后接函数名称,括号内则是传入函数中的参数 (每一个参数的类型以及名称要依次写出,没有参数则不写),"returns"后接该函数返回值的类型(无返回值则可以不写returns)。

⑤用constant、view、pure修饰function分别表示:

constant:只能读取、不可改变状态变量(就是contract中定义的变量)

view:只能读取、不可改变状态变量,和constant一样,不消耗燃料

pure:不能读取也不能改变状态变量,一个固定的输入只能有一个固定的输出

[1]以下情况被认为是修改状态:修改状态变量(函数体外部、合约内部创建的变量就是状态变量)、产生事件、创建其它合约、使用selfdestruct、通过调用发送以太币、调用任何没有标记为view或者pure的函数、使用低级调用、使用包含特定操作码的内联汇编。

[2]以下被认为是从状态中进行读取:读取状态变量、访问this.balance或者 <address>.balance、访问block,tx,msg中任意成员(除msg.sig和msg.data之外)、调用任何未标记为pure的函数、使用包含某些操作码的内联汇编。

⑥用external、public、internal、private修饰function分别表示:

external:外部函数作为合约接口的一部分,意味着可以从其他合约和交易中调用, 一个外部函数f不能从内部调用 (即f不起作用,但this.f()可以,这些后续都会详细介绍),当收到大量数据的时候,外部函数有时候会更有效率。

public:public函数是合约接口的一部分,可以在内部或通过消息调用对于public状态变量,会自动生成一个getter函数

internal:这些函数和状态变量只能是内部访问(即从当前合约内部或从它派生的合约 访问),不使用this调用。

private:private函数和状态变量仅在当前定义它们的合约中使用,并且不能被派生合 约使用。

⑦solidity中函数可以递归调用。

⑧solidity可以使用import导入其它源文件:

[1]import "filename"; //从"filename"中导入所有的全局符号到当前全局作用域中

[2]import * as symbolName from "filename"; //创建一个新的全局符号symbolName,其成员均来自"filename"中的全局符号

[3]import {symbol1 as alias, symbol2} from "filename"; //创建新的全局符号alias和symbol2,分别从"filename"引用symbol1和symbol2

[4]import "filename" as symbolName; //这条语句等同于import * as symbolName from "filename";

2、语法------逻辑运算:

javascript 复制代码
//声明版本号(程序中的版本号要和编译器版本号一致)
pragma solidity ^0.5.17;
//合约
contract BoolTest
{
    
    bool a;  //创建布尔类型变量时如不初始化,默认赋为false
    
    function geta() public returns(bool)
    {
        return a;
    }

    function getnota() public returns(bool)
    {
        return !a;  //a当前为false,经过非运算后会返回true
    }

    int c = 1;
    int d = 2;
    
    //(这部分与c语言几乎完全相同,就不列举所有情形了)
    function cdequal() public returns(bool)
    {
        return c==d;  //c与d当前不相等,c==d不成立,会返回false
    }
    
    function cdequalAnd() public returns(bool)
    {
        return c==d && true;  //c==d返回false,false与true进行与运算,结果为false
    }
    
    function cdequalOr() public returns(bool)
    {
        return c==d || true;  //c==d返回false,false与true进行或运算,结果为true
    }
    
    function cdnotequalAnd() public returns(bool)
    {
        return c!=d && true;  //c与d当前不相等,c!=d成立,返回true,true与true进行或运算,结果为true
    }
}

3、语法------整型与算术运算:

javascript 复制代码
//声明版本号(程序中的版本号要和编译器版本号一致)
pragma solidity ^0.5.17;
//合约
contract MathTest
{
    //加(a与b为传入方法中的参数,下同理)
    function add(uint a,uint b) public returns(uint)
    {
        return a+b;
    }
    //减
    function minus(uint a,uint b) public returns(uint)
    {
        return a-b;
    }
    //乘
    function multiply(uint a,uint b) public returns(uint)
    {
        return a*b;
    }
    //除
    function divide(uint a,uint b) public returns(uint)
    {
        return a/b;
    }
    //取余运算
    function mod(uint a,uint b) public returns(uint)
    {
        return a%b;
    }
    //幂运算
    function square(uint a,uint b) public returns(uint)
    {
        return a**b;
    }
}

4、语法------位运算:

javascript 复制代码
//声明版本号(程序中的版本号要和编译器版本号一致)
pragma solidity ^0.5.17;
//合约
contract MathTest
{
    //uint8表示大小为8个位的无符号整型
    uint8 a = 3;  //二进制表示为00000011
    uint8 b = 4;  //二进制表示为00000100

    //按位与(该部分和c语言也几乎完全相同)
    function bitwiseAnd() public returns(uint8)
    {
        return a&b;
    }
    //按位或
    function bitwiseOr() public returns(uint8)
    {
        return a|b;
    }
    //按位取反
    function tilde() public returns(uint8)
    {
        return ~a;
    }
    //按位异或
    function caret() public returns(uint8)
    {
        return a^b;
    }
    //左移(左移运算符后接左移的位数)
    function leftShift() public returns(uint8)
    {
        return a<<1;
    }
    //右移(右移运算符后接右移的位数)
    function rightShift() public returns(uint8)
    {
        return a>>1;
    }
}

(图源:https://blog.csdn.net/aiwaston/article/details/113665723

5、语法------复合运算:

javascript 复制代码
//声明版本号(程序中的版本号要和编译器版本号一致)
pragma solidity ^0.5.17;
//合约
contract MathTest
{
    uint a = 1;
    //该语法也和c语言基本完全相同
    //输出a
    function add1() public returns(uint)
    {
        return a++;  //执行完该语句后a的值会加1
    }
    //输出a+1
    function add2() public returns(uint)
    {
        return ++a;  //执行该语句前a的值会加1,然后再执行该语句
    }
    //输出a
    function sub1() public returns(uint)
    {
        return a--;  //执行完该语句后a的值会减1
    }
    //输出a-1
    function sub2() public returns(uint)
    {
        return --a;  //执行该语句前a的值会减1,然后再执行该语句
    }
}

6、现象------整型溢出:

javascript 复制代码
//声明版本号(程序中的版本号要和编译器版本号一致)
pragma solidity ^0.5.17;
//合约
contract MathTest
{
    //这部分也与c语言几乎完全相同
    function flow() view public returns(uint8)
    {
        uint8 mm  = 255;  //二进制为11111111
        mm++;
        return mm;  //mm只有8位,但是对于mm,再加1会得到100000000,只能取前8位,于是返回0而不是256
    }
    
    function flow2() view public returns(uint256)
    {
        uint8 mm  = 255;
        mm++;
        return mm;  //即使返回值声明有256位,但是在"mm++"这一步mm就被修改为0,所以返回的还是0
    }
    
     function flow3() view public returns(uint)
     {
        uint mm  = 255;  //这次没对mm的位数进行限制,它可以装下100000000
        mm++;
        return mm;
    }

    function flow4() view public returns(uint8){
        uint8 nn = 0;
        nn--;
        return nn;  //对00000000做减法,会先给"第九位"添1(要明确nn的位数),那么就会得到结果11111111,转换成十进制就是255(对于有符号数,就涉及到了补码的运算,同理,先转换成补码按此规律运算,再转换回十进制)
    }
}

7、现象------非法运算:

javascript 复制代码
//声明版本号(程序中的版本号要和编译器版本号一致)
pragma solidity ^0.5.17;
//合约
contract MathTest
{
    function errorTest() view public returns(int)
    {
        int a = 2;
        int b = 3;
        return a/b;  //既然声明了返回值为整型,那么就不可能得到小数
        //不过solidity允许整型运算的过程中存在小数,只是结果不能接纳小数,比如2/5-2/5
        //solidity会将表达式计算出结果后再按照一定的规则(比如整形溢出等)进行处理,比如2**99-2**99+1
    }
    
    function errorTest2() view public returns(int)
    {
        int a = 2;
        int b = 0;
        return a/b;  //0不能做除数,这个方法甚至不能成功编译
    }

    function errorTest3() view public returns(int)
    {
        int a = -1;
        int b = 0;
        return b >> a;  //位移运算符不接受负数
    }
    
}

8、语法------固定长度字节数组:

javascript 复制代码
//声明版本号(程序中的版本号要和编译器版本号一致)
pragma solidity ^0.5.17;
//合约
contract ByteArray
{
    bytes1 public num1 = 0x7a;  //1个字节长度的变量
    
    bytes2 public num2 = 0x7a68;  //2个字节长度的变量
    
    bytes12 public num3 = 0x7a68656e676a69616e78756e;  //12个字节长度的变量
}

(1)在solidity中,直接用public声明成员变量,编译部署后,会生成一个默认的get方法,通过该方法可以直接查看这个成员属性,而不需要通过自拟函数返回这个成员属性。

(2)字节数组同样支持位运算以及逻辑运算。

(3)固定长度字节数组不可以仅仅对某个字段进行更改 (可以仅对某个字段进行读取,如下表所示),要么就直接对其整体重新赋值

|----|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|------------|------------|
| 名称 | num3[0] | num3[1] | num3[2] | num3[3] | num3[4] | num3[5] | num3[6] | num3[7] | num3[8] | num3[9] | num3[10] | num3[11] |
| 内容 | 0x7a | 0x68 | 0x65 | 0x6e | 0x67 | 0x6a | 0x69 | 0x61 | 0x6e | 0x78 | 0x75 | 0x6e |

(4)定长字节数组的关键字有bytes1,bytes2,bytes3,...,bytes32。

(5)不管是定长数组还是动态长度数组,都不能越界访问,否则会报错。

9、语法------动态长度字节数组:

javascript 复制代码
//声明版本号(程序中的版本号要和编译器版本号一致)
pragma solidity ^0.5.17;
//合约
contract DynamicByteArray
{
    
    bytes public name = new bytes(2);  //为动态字节数组开辟2个字节的空间
    
    function initName() public
    {
        name[0] = 0x7a;  //对动态长度字节数组进行初始化
        name[1] = 0x68;
    }
    
    function getLength() view public returns(uint)
    {
        return name.length;  //8个位(一个字节)为一个长度单位
    }
    
    function changeName() public
    {
        name[0] = 0x88;  //可以对某一字段进行修改
    }
    
    function changeLength() public
    {
        name.length = 5;  //可以修改数组的长度,不过修改的同时会将数组的内容全部置为0
    }

    function pushTest() public
    {
        name.push(0x99);  //可以在数组末尾追加字节元素,同时增加数组的长度
    }
}

(1)动态 长度字节数组可以仅对某个字段进行更改

(2)可以通过修改动态长度数组的length属性修改动态长度数组的长度,不过修改的同时会将数组的内容全部置为0。

(3)动态长度数组有push方法,使用该方法可以在数组末尾追加指定的字节元素,同时增加数组的长度。

相关推荐
qiquandongkh3 小时前
2025年股指期货和股指期权合约交割的通知!
大数据·金融·区块链
yoona10201 天前
Rust编程语言入门教程 (七)函数与控制流
开发语言·rust·区块链·学习方法
MetaverseMan2 天前
从sumsub获取用户图片
区块链
电报号dapp1193 天前
区块链虚拟币资产去中心化私钥钱包开发
人工智能·去中心化·区块链·智能合约
漠缠3 天前
股票与比特币投资困境分析及解决方案
人工智能·区块链
AC使者3 天前
解释区块链技术的应用场景和优势。
区块链
YSGZJJ3 天前
怎么查股指期货持仓量?
区块链
yoona10204 天前
Rust编程语言入门教程(三) Hello Cargo
开发语言·后端·rust·区块链·学习方法
yoona10206 天前
Rust编程语言入门教程(二)hello_world
开发语言·后端·rust·区块链·学习方法