一、Solidity 是什么
- Solidity是一门面向合约的、为实现智能合约而创建的高级编程语言。这门语言受到了C++,Python和JavaScript语言的影响,设计的目的是能在以太坊虚拟机(EVM)上运行。
- Solidity 是静态类型语言,支持继承、库和复杂的用户定义类型等特性。
- 内含的类型除了常见的编程语言的标准类型,还包括address等以太坊独有的类型,Solidity源码文件通常以sol作为扩展名。
- 目前尝试Solidity编程最好的方式是使用Remix。Remis是一个基于Web浏览器的IDE,它可以让你编写Solidity智能合约,然后部署并运行该智能合约。
二、Solidity语言特性
?Solidity的语法接近于JavaScript,是一种面向对对象的语言。但作为一种真正意义上裕兴在网络上的去中心化合约,它又有很多的不同:
- 以太坊底层基于账户,而不是UTXO,所以增加了一个特殊的address的数据类型用于定义用户和合约账户。
- 语言内嵌框架支持支付。提供了payable等关键字,可以在语言层面直接支持支付。
- 使用区块链进行数据的存储。数据的每一个状态都可以永久存储,所以在使用时需要确定变量使用内存,还是区块链存储。
- 运行环境是在去中心化的网络上,所以需要强调合约或函数执行的调用方式。
- 不同的异常机制。一旦出现异常,所有的执行都将会被回撤,这主要是为了保证合约执行的原子行,以避免中间状态出现的数据不一致。
三、Solidity源码和智能合约
Solidity源代码要成为可以运行在以太坊上的智能合约需要经历如下的步骤:
- 用Solidity编写的只能合约源代码需要先使用编译器编译为字节码(Bytecode),编译过程中会同时产生智能合约的二进制接口规范(Application Binary Interface,简称ABI)。
- 通过交易(Transaction)的方式将字节码部署到以太坊网络,每次成功部署都会产生一个新的智能合约账户。
- 使用Javascript 编写的Dapp通常通过web3.js + ABI去调用智能合约中的函数来实现数据的读取和修改。
四、Solidity编译器
? ? ? ? Remix
- Remix是一个基于Web浏览器的Solidity的IDE,可以在线使用而无需安装任务东西。
- http://remix.ethereum.org
? ? ? ? solcjs
- solc是Solidity源码库的构建目标之一,她是Solidtiy的命令行编译器
- 使用npm 可以便捷地安装Solidtity编译器solcjs
- npm install -g solc
五、一个简单的数据存储智能合约
pragma solidity >=0.7.0 <0.9.0;
contract SimpleStorage {
uint myData;
function setMyData(uint newData) public {
myData = newData;
}
function getMyData() public view returns(uint) {
return myData;
}
}
通过Remix和MetaMask可以直接进行编译和部署。
?在部署后,可以在Remix中直接调用函数进行结果测试。
六、Solidity一个子货币案例
// 设置solidity版本
pragma solidity >=0.7.0 <0.9.0;
// contract 关键字定义合约 ,类似于java中的calss
contract Coin{
// address是solidity中的特殊的数据类型
address public mointer;
mapping(address => uint) banlances;
event Sent(address from, address to, uint amount);
constructor(){
mointer = msg.sender;
}
function mint(address receiver,uint amount) public {
require(mointer == msg.sender);
banlances[receiver] += amount;
}
function send(address receiver, uint amount) public {
require(amount <= banlances[msg.sender]);
banlances[msg.sender] -= amount;
banlances[receiver] += amount;
// emit Sent(msg.sender, receiver, amount);
}
function getBanlance(address viewer) public view returns(uint) {
return banlances[viewer];
}
}
七、Solidity源文件布局
?1、pragma(版本杂注)
- 源文件可以被版本杂注pragma所注解,表明要求的编译器版本
- 例如:pragma solidtiy ^0.4.0;
- 源文件将既不允许低于0.4.0版本的编译器编译,也不允许高于(包含)0.5.0版本的编译器编译(第二个条件因使用^被添加)
2、import (导入其它源文件)
?Solidity所支持的导入语句import,语法同JavaScript非常类似。
八、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引用类型
1、数组(Array)
数组可以在声明时指定长度(定长数组),也可以动态调整大小(变长数组、动态数组);
对于存储(storage)的数组来说,元素类型可以是任意的(即元素可以数组类型、映射类型或者结构体);对于内存型(memory)的数组来说,元素类型不能是映射(mapping)类型的
2、结构(struct)
Solidity支持通过Struct新的结构体的形式定义新的类型。相当于Java中的实体类的概念。
3、映射(Mapping)
类似于Java中的Map, 映射可以视作哈希表,在实际的初始化过程中创建每个可能的key,并将其映射到字节形式全是零的值(类型默认值)。
十、Solidity地址类型
1 address
地址类型存储一个20字节的值(以太坊地址的大小);地址类型也有成员变量,并作为所有合约的基础。
2、address payable(0.5.0引入)
与地址类型基本相同,不过多出了transfer 和 send两个成员变量。
3、两者区别和转换
- Payable地址是可以发送ehter的地址,而普通address不能。
- 允许从payable address到address的隐私换砖,而反过来的直接转换是不可能的(在0.7.0版本中可以通过payable 进行中间转换)
- 从0.5.0版本起,合约不再是从地址类型派生而来,但如果它有payable的回退函数,那同样可以显式转换Wieaddress 或者address payable类型。
十一、地址类型成员变量
? ? ? ?1、?<address>.balance
? ? ? ? ?获取该地址的ether余额, 以wei为单位。
? ? ? ? 2、<address payable>.transfer(uint256 amount)
? ? ? ? 向指定地址发送数量为amount 的ether(以wei为单位),失败时抛出异常,发送2300 gas的矿工费,不可调节。谁调用transfer 就给谁发币。
? ? ? ? 3、<adress.payable>.send(uint256 amount )returns(bool)
? ? ? ? ? ? ? ? 向指定地址发送数量为amount的ether(以wei为单位),失败时返回false,发送2300gas旷工费用,不可调节。
? ? ? ? 4、<address>.call(bytes memory)returns(bool,bytes memory)
? ? ? ? ? ? ? ? 发出底层函数调用call,失败时返回false,发送所有可用gas,可调节
? ? ? ? 5、<address>.delegatecall(bytes memory)
????????????????发出底层函数调用call,失败时返回false,发送所有可用gas,可调节
????????6、<address>.staticcall(bytes memory)
?????????发出底层函数调用call,失败时返回false,发送所有可用gas,可调节
十二、地址成员变量用法:
1、balance和transfer
?可以使用balance 属性来查询一个地址的余额,使用使用transfer函数像一个payable地址发送以太币Ether(以wei为单位)
address payable x = address(0x123);
address myAddress = address(this);
if (x.banlance < 10 && myAddress.balance >= 10)
x.transfer(10);
2、send
? ? ? send是transfer的低级版本,如果执行失败,当前的合约不会因为异常而终止,但send会返回false。
3、call
? ? ? ? 也可以用call来实现抓币的操作,通过添加gas()和value()修饰器。
nameReg.call.gas(1000000).value(1 ether)("register", "MyName");
call函数都属于低级函数,需要谨慎使用。 具体来说,任何未知的合约都可能是恶意的。 你在调用一个合约的同时就将控制权交给了它,它可以反过来调用你的合约, 因此,当调用返回时要为你的状态变量的改变做好准备。
十三、函数可见性修饰
? ?由于Solidity有两种函数调用方式,内部调用和外部调用。内部调用不会产生实际的EVM调用活称为消息调用,而外部调用则会产生一个EVM调用。函数和状态变量有四种可见性类型。函数可以指定为external、public、internal、或者private,默认情况下函数类型为public。对于状态变量,不能设置为external,默认为internal。
1、external:
? ? ? ?函数只能通过合约外的函数调用,不能通过本合约内的函数调用。
2、public:
? ? ? ? public函数是合约接口的一部分,任何合约都可以调用。对于public修饰的状态变量,会自动生成一个getter函数。
3、internal:
? ? ? ? ?internal修饰的函数和状态变量只能在内部访问,不适用this调用。
4、private
????????private修饰的函数和状态变量仅在当前合约中使用,并且不能被派生合约 使用。
十四、函数状态类型
? ?1、view函数
? ? ? ? 可以将函数声明为view类型,被view修饰的函数要保证不修改状态。
? ? ?2、Pure函数(纯计算)
? ? ? ? 函数可以声明为Pure,在这种情况下,承诺不读取或修改装填。
? ? 3、Fallback函数
? ? ? ? 合约可有一个未命名的函数。这个函数不能有参数也不能有返回值。如果在一个合约的调用中,没有其他函数与给定的函数标识符匹配,那么这个函数(fallback 函数)会被执行。
? ? ? ? 除此之外,每当合约收到以太币(没有任何数据),这个函数就会执行。此外为了接收以太币,fallback函数必须标记为payable。如果不存在这样的函数,则合约不能通过常规交易接收以太币。
? ? ? ? 在这样的上下文中,通常只有很少的gas可以用来完成这个函数调用(准确的说,是2300gas),所以使fallback函数的调用尽量廉价很重要。请注意,调用fallback函数的交易,(而不是内部调用)所需的gas要高的多,因为每次交易都会额外收取21000 gas或更多的费用,用于签名检查等操作。(这里不明白什么意思)
十五、函数修饰器
? 使用修饰器可以轻松改变函数的行为。例如,他们可以在执行函数之前自动检查某个条件。修饰器是合约的可集成属性,并可能被派生合约覆盖。
pragma solidity ^0.4.11;
contract owned {
function owned() public { owner = msg.sender; }
address owner;
// 这个合约只定义一个修饰器,但并未使用: 它将会在派生合约中用到。
// 修饰器所修饰的函数体会被插入到特殊符号 _; 的位置。
// 这意味着如果是 owner 调用这个函数,则函数会被执行,否则会抛出异常。
modifier onlyOwner {
require(msg.sender == owner);
// _是一个占位符
_;
}
}
contract mortal is owned {
// 这个合约从 `owned` 继承了 `onlyOwner` 修饰符,并将其应用于 `close` 函数,
// 只有在合约里保存的 owner 调用 `close` 函数,才会生效。
function close() public onlyOwner {
selfdestruct(owner);
}
}
有的语法在新版本中会有出入,后面还有新增会持续添加...
|