1. Solidity简介
Solidity是一种静态类型编程语言,专门用于在以太坊区块链上编写智能合约。它借鉴了JavaScript、Python和C++的语法,非常适合开发在以太坊虚拟机(EVM)上运行的应用程序。
智能合约:表达商业、法律关系的契约,如:DeFi中表达为投资人和金融组织之间的关系,投资人有什么样的权益,做资产交换,遵守什么样的过程
运行机制:
来一次transaction驱动一次EVM,transaction是序列的形成,故EVM不会出现并发问题
transaction要么成功或失败,失败会回退到transaction开始之前的状态
2. 环境设置
在编写Solidity合约之前,需要安装一些必要的工具:
Remix IDE:这是一个基于浏览器的IDE,专门用于编写、编译和部署Solidity智能合约。可以通过访问Remix来使用。
Node.js和npm:用于安装和管理以太坊开发工具,如Truffle和Ganache。
在线工具:https://remix.ethereum.org/
3. 基本结构
一个Solidity合约的基本结构如下:
pragma solidity >=0.4.16 <0.8.0;// 声明Solidity版本contract ZombieFactory {//定义合约// 这里建立事件event NewZombie(uint zombieId, string name, uint dna);uint dnaDigits = 16;//uint dnaModulus = 10 ** dnaDigits;//结构体struct Zombie {string name;uint dna;}// 动态创建公共数组Zombie[] public zombies;//public 数组, Solidity 会自动创建 getter 方法// 函数function _createZombie(string _name,uint _dna) private {zombies.push(Zombie(_name, _dna));// 这里触发事件uint id = zombies.push(Zombie(_name,_dna))-1;emit NewZombie(id, _name, _dna);}function _generateRandomDna(string _str) private view returns (uint) {uint rand = uint(keccak256(_str));return rand % dnaModulus;}function createRandomZombie(string _name) public {uint randDna = _generateRandomDna(_name);_createZombie(_name, randDna);}// 构造函数constructor(uint _initialNumber, string memory _initialString) {myNumber = _initialNumber;myString = _initialString;}}
4. 详细解释
4.1 版本声明
pragma solidity ^0.8.0;
4.2 合约定义
contract ZombieFactory {...
}
4.3 状态变量(也是成员变量)
uint dnaDigits = 16;//
uint dnaModulus = 10 ** dnaDigits;
变量的可见性:合约外部,本合约,子合约
public:完全可见;
private:本合约内部可见,合约的里函数都是可以使用的;
其他不可见internal:对继承子合约可见
4.4 结构体
结构体是创建复杂(有多种数据类型的集合)的数据类型,和C的结构体一样
struct Zombie {string name;uint dna;}
4.5 数组
Solidity 支持两种数组: 静态 数组和_动态_ 数组:
// 固定长度为2的静态数组:
uint[2] fixedArray;
// 固定长度为5的string类型的静态数组:
string[5] stringArray;
// 动态数组,长度不固定,可以动态添加元素:
uint[] dynamicArray;
4.6 构造函数
// 合约在区块链的部署时,产生构造函数,默认public
constructor(uint _initialNumber, string memory _initialString) {myNumber = _initialNumber;myString = _initialString;}
4.7 函数
function _createZombie(string _name,uint _dna) private {zombies.push(Zombie(_name, _dna));// 这里触发事件uint id = zombies.push(Zombie(_name,_dna))-1;NewZombie(id, _name, _dna);}function createRandomZombie(string _name) public {uint randDna = _generateRandomDna(_name);_createZombie(_name, randDna);}function 函数名(参数) [可见性][交易相关性][...] returns{...}函数可见性
默认值的变化:刚开始public,现在取消了
private
public
internal 和 private 类似,不过, 如果某个合约继承自其父合约,这个合约即可以访问父合约中定义的“内部”函数。
external 与public 类似,只不过这些函数只能在合约之外调用 - 它们可以被合约内的其他函数调用,但是会产生新的message。合约函数的交易属性:view:用于读取合约状态的函数,不能修改状态
pure:纯函数,与合约状态无关,只和输入有关
默认是写操作:全网广播,共识确认
4.8 结构体和数组
// 创建一个新的Person:
Person satoshi = Person(172, "Satoshi");// 将新创建的satoshi添加进people数组:
people.push(satoshi);
你也可以两步并一步,用一行代码更简洁:people.push(Person(16, "Vitalik"));
注:array.push() 在数组的 尾部 加入新元素 ,所以元素在数组中的顺序就是我们添加的顺序, 如:uint[] numbers;
numbers.push(5);
numbers.push(10);
numbers.push(15);
// The `numbers` array is now equal to [5, 10, 15]
5、数据类型
5.1 基本数据类型
● 布尔类型(bool):布尔类型表示真或假的值,默认值是 false。
● 整数类型:整数类型分为有符号和无符号两种。有符号整数类型包括int8、int16、int32、int64等,而无符号整数类型包括uint8、uint16、uint32、uint64等。这些类型表示不同位数的整数,默认值是0。
● 固定点数类型:固定点数类型用于处理小数。例如,fixed和ufixed表示固定点数,后面可以跟着小数点的位数。
● 地址类型(address):地址类型表示以太坊网络上的账户地址(钱包的地址)。默认值是0x0000000000000000000000000000000000000000
● 补充:
● 字节类型(bytes):字节类型表示一组字节数据。例如,bytes32表示32个字节的数据。
● 动态字节数组类型(bytes):动态字节数组类型与字节类型类似,但其长度可变。
● 字符串类型(string):字符串类型表示文本数据。默认值是空字符串
● 枚举类型(enum):枚举类型用于定义一组离散的可能取值。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;contract MyContract {bool public myBool; // 默认值为 falseuint public myUint; // 默认值为 0address public myAddress; // 默认值为 0x0000000000000000000000000000000000000000string public myString; // 默认值为 ""uint[] public myArray; // 默认值为一个空的、长度为 0 的数组
}
动态数组(包括字符串数组)和映射(mapping)的默认值是一个空的、长度为0的集合补充:
address payable 是一种特殊的 address 类型,它可以用来接收和发送以太币。
address 类型则是一个普通的地址,只能接收以太币但不能主动发送。
由于 address payable 是 address 的子类型,
因此可以从 address payable 转换为 address,但反过来则不行。pragma solidity ^0.8.0;contract ConversionExample {address public addr;constructor() {address payable payableAddr = msg.sender;addr = payableAddr; // 隐式转换addr = address(_payableAddr); // 显示转换}
}
5.2、gas消耗
每次调用这个store函数,我们都会发送一个交易。因为每次在更改区块链状态的时候,我们都会发送交易,可以在右下角Remix的日志区域,查看交易细节。
你可以看到交易消耗了多少gas,可以看到这里的数字比发送交易所用到的21000 gas要多的,那是因为这里的操作会消耗更多的计算量。每个区块链计算gas的方式不同,但最简单的理解是,做越多的操作,消耗更多的gas。记住,只有更改状态的时候才支付gas