1 数据存储位置
数据测存储类型有storage 和 memory 函数的传入参数和返回参数 都是 memory类型(external函数的入参为calldata类型,只可读,不可重写) 函数局部变量、合约状态变量都是storage类型
memory总是会重新分配内存,不会释放内存,当有重名未使用的变量时,不会利用前者内存空间,而会重新分配新的空间 memory数据的位置标注可以修改,数据可读可修改
storage存储在EVM的storage区域,在交易执行之后,storage区域的数据会写回到合约账户的storage中 EVM的slot是32字节,因此在存储storage变量的时候,变量值小于32字节时会把多个变量打包存放在一个slot中。大于32字节的数据,slot中存放的是数据字节长度+长度值的字节数 多变量打包规则: 每一项都按低位对齐 基本数据类型选择合适的字节命名 struct和array会从一个新的slot开始存储,但是strut和array内部的数据还是按照上述打包规则打包存入slot 例如:uint128 a;uint256 b;uint128 c; 按照多量打包的规则应该写为: uint128 a; uint128 c;uint256 b;
calldata的数据类型,标识的数据不能被修改也不能持久化 只能用于external函数的入参
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.4.0;
contract CalldataContract {
function calldataTest(uint8 [] array) external pure returns (uint8) {
assert(array.length > 0 && array.length <= 10);
uint8 sum = 0;
// array[0] = 10; //external的入参数据类型为calldata 只读不可修改
for(uint8 i=0;i<array.length;i++){
sum += array[i];
}
return sum;
}
function memoryTest(uint8 [] array) public pure returns (uint8) {
assert(array.length > 0 && array.length <= 10);
uint8 sum = 0;
array[0] = 10; //array数据类型为storage存储
for(uint8 i=0;i<array.length;i++){
sum += array[i];
}
return sum;
}
}
2 变量赋值
|数据类型|执行操作 | |-----|–| |1| 状态变量->状态变量 | 拷贝操作 | |2| 状态变量->局部storage变量 | 指针(引用) | |3| 状态变量->memory数据 | 拷贝操作 | |4| 局部memory数据->状态变量 | 拷贝操作 | |5| 局部memory数据->局部memory数据 | 指针拷贝(引用拷贝) | |6| 局部memory数据->局部storage数据 | X不能进行转换 | |7| 局部storage数据->状态变量 | 拷贝操作 | |8| 局部storage数据->局部memory变量 | 拷贝操作 | |9| 局部storage数据->局部storage变量 | 指针拷贝(引用拷贝) | 拷贝操作中 B数据的修改不会影响A 指针操作中 B数据的修改会影响A
//状态变量赋值给状态变量 执行拷贝操作
contract StateToStateContract {
uint8 [3] public arrayF = [1,2,3];
uint8 [3] public arrayS;
event LogUint8(uint8);
function changeArray () public {
uint8 i = 0;
for(;i<3;i++){
emit LogUint8(arrayF[i]);
}
arrayS = arrayF;
arrayF[0] = 10;
i=0;
for(;i<3;i++){
emit LogUint8(arrayS[i]);
}
}
}
/**
* 状态变量赋值给局部storage变量 执行引用传递指针操作
**/
contract StateToStateContract {
uint8 [3] public arrayF = [1,2,3];
event LogUint8(uint8);
function changeArray () public {
uint8 [3] storage arrayS;
uint8 i = 0;
for(;i<3;i++){
emit LogUint8(arrayF[i]);
}
arrayS = arrayF;
arrayF[0] = 10;
i=0;
for(;i<3;i++){
emit LogUint8(arrayS[i]);
}
}
}
/**
* 状态变量赋值给局部memory变量 执行拷贝操作
**/
contract StateToStateContract {
uint8 [3] public arrayF = [1,2,3];
event LogUint8(uint8);
function changeArray () public {
uint8 [3] memory arrayS;
uint8 i = 0;
for(;i<3;i++){
emit LogUint8(arrayF[i]);
}
arrayS = arrayF;
arrayF[0] = 10;
i=0;
for(;i<3;i++){
emit LogUint8(arrayS[i]);
}
}
}
/**
* 局部memory变量赋值给局部memory变量 执行引用传递指针操作
**/
contract StateToStateContract {
event LogUint8(uint8);
function changeArray () public {
uint8 [3] memory arrayF = [1,2,3];
uint8 [3] memory arrayS;
uint8 i = 0;
for(;i<3;i++){
emit LogUint8(arrayF[i]);
}
arrayS = arrayF;
arrayF[0] = 10;
i=0;
for(;i<3;i++){
emit LogUint8(arrayS[i]);
}
}
}
/** 局部memory变量赋值给状态变量 执行拷贝操作 **/
/** 局部storage变量赋值给状态变量 执行拷贝操作 **/
contract StateToStateContract {
uint8 [3] public arrayF = [1,2,3];
uint8 [3] public arrayS;
event LogUint8(uint8);
function changeArray () public {
uint8 [3] storage array = arrayF;
arrayS=array;
uint8 i = 0;
for(;i<3;i++){
emit LogUint8(arrayF[i]);
}
array[0] = 10;
i=0;
for(;i<3;i++){
emit LogUint8(arrayS[i]);
}
}
}
/** 局部storage变量赋值给局部storage变量 执行指针引用操作 **/
/** 局部storage变量赋值给局部memory变量 执行拷贝操作 **/
3 函数修改器
modifier修饰符标识函数修改器 有函数名称 可以接受参数,方法内部的_;为修饰函数插入的地方 用来检查函数运行的前置条件和后置的清理工作 示例代码: 判断当前语句的执行地址需要是合约部署的账户地址 在切换账户后,再次执行应该报错
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.4.0;
contract ModifierContract {
uint public a = 0;
address owner = msg.sender;
event LogStringUint(string id,uint data);
modifier modifierA(uint arga) {
emit LogStringUint("modifierA before a=:",a);
emit LogStringUint("modifierA before arga=:",arga);
//identity msg
require(owner==msg.sender,"not satisfify identity");
_;
emit LogStringUint("modifierA after a=:",a);
emit LogStringUint("modifierA after arga=:",arga);
}
function modifierTest () public modifierA(a) returns(uint res) {
emit LogStringUint("modifierTest1 a:",a);
a = 5;
emit LogStringUint("modifierTest2 a:",a);
return a;
}
}
切换账户执行合约 不满足require判断,会报错
一个函数可以使用多个修改器验证前置条件,前置条件的验证顺序按照修改器顺序执行,后置条件的清理按照修改器逆向顺序执行 即前置和后置条件的执行顺序相反,先执行前置条件的最后执行后置条件
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.4.0;
contract ModifierSummaryContract {
uint public a = 0;
event LogStringUint(string id,uint data);
modifier modifierA(uint arga) {
emit LogStringUint("modifierA before a=:",a);
emit LogStringUint("modifierA before arga=:",arga);
_;
emit LogStringUint("modifierA after a=:",a);
emit LogStringUint("modifierA after arga=:",arga);
}
modifier modifierB {
emit LogStringUint("modifierB before a=:",a);
// return;
_;
emit LogStringUint("modifierB after a=:",a);
}
function modifierTest () public modifierA(10) modifierB returns(uint res) {
emit LogStringUint("modifierTest1 a:",a);
a = 5;
emit LogStringUint("modifierTest2 a:",a);
return a;
}
}
执行的日志:
[ { “from”: “0x417Bf7C9dc415FEEb693B6FE313d1186C692600F”, “topic”: “0xfb812593d6d1d6fbf5e981f61205a76acc217dd9dcc7b95c4f58247fb09f2658”, “event”: “LogStringUint”, “args”: { “0”: “modifierA before a=:”, “1”: “0”, “id”: “modifierA before a=:”, “data”: “0” } }, { “from”: “0x417Bf7C9dc415FEEb693B6FE313d1186C692600F”, “topic”: “0xfb812593d6d1d6fbf5e981f61205a76acc217dd9dcc7b95c4f58247fb09f2658”, “event”: “LogStringUint”, “args”: { “0”: “modifierA before arga=:”, “1”: “10”, “id”: “modifierA before arga=:”, “data”: “10” } }, { “from”: “0x417Bf7C9dc415FEEb693B6FE313d1186C692600F”, “topic”: “0xfb812593d6d1d6fbf5e981f61205a76acc217dd9dcc7b95c4f58247fb09f2658”, “event”: “LogStringUint”, “args”: { “0”: “modifierB before a=:”, “1”: “0”, “id”: “modifierB before a=:”, “data”: “0” } }, { “from”: “0x417Bf7C9dc415FEEb693B6FE313d1186C692600F”, “topic”: “0xfb812593d6d1d6fbf5e981f61205a76acc217dd9dcc7b95c4f58247fb09f2658”, “event”: “LogStringUint”, “args”: { “0”: “modifierTest1 a:”, “1”: “0”, “id”: “modifierTest1 a:”, “data”: “0” } }, { “from”: “0x417Bf7C9dc415FEEb693B6FE313d1186C692600F”, “topic”: “0xfb812593d6d1d6fbf5e981f61205a76acc217dd9dcc7b95c4f58247fb09f2658”, “event”: “LogStringUint”, “args”: { “0”: “modifierTest2 a:”, “1”: “5”, “id”: “modifierTest2 a:”, “data”: “5” } }, { “from”: “0x417Bf7C9dc415FEEb693B6FE313d1186C692600F”, “topic”: “0xfb812593d6d1d6fbf5e981f61205a76acc217dd9dcc7b95c4f58247fb09f2658”, “event”: “LogStringUint”, “args”: { “0”: “modifierB after a=:”, “1”: “5”, “id”: “modifierB after a=:”, “data”: “5” } }, { “from”: “0x417Bf7C9dc415FEEb693B6FE313d1186C692600F”, “topic”: “0xfb812593d6d1d6fbf5e981f61205a76acc217dd9dcc7b95c4f58247fb09f2658”, “event”: “LogStringUint”, “args”: { “0”: “modifierA after a=:”, “1”: “5”, “id”: “modifierA after a=:”, “data”: “5” } }, { “from”: “0x417Bf7C9dc415FEEb693B6FE313d1186C692600F”, “topic”: “0xfb812593d6d1d6fbf5e981f61205a76acc217dd9dcc7b95c4f58247fb09f2658”, “event”: “LogStringUint”, “args”: { “0”: “modifierA after arga=:”, “1”: “10”, “id”: “modifierA after arga=:”, “data”: “10” } } ]
修改器中可以包含return ,return语句之后的修改器不触发 再修改器函数return后,执行return之前的修改器的后置清理条件
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.4.0;
contract ModifierSummaryContract {
uint public a = 0;
event LogStringUint(string id,uint data);
modifier modifierA(uint arga) {
emit LogStringUint("modifierA before a=:",a);
emit LogStringUint("modifierA before arga=:",arga);
_;
emit LogStringUint("modifierA after a=:",a);
emit LogStringUint("modifierA after arga=:",arga);
}
modifier modifierB {
emit LogStringUint("modifierB before a=:",a);
//return;
_;
emit LogStringUint("modifierB after a=:",a);
}
modifier modifierC {
emit LogStringUint("modifierC before a=:",a);
return; //return之后的语句不在执行,返回执行前面两个修饰器的后置条件
_;
emit LogStringUint("modifierC after a=:",a);
}
//C中有return 执行顺序 A前置 B前置 Creturn 之前的语句 B后置 A后置
function modifierTest () public modifierA(10) modifierB modifierC returns(uint res) {
emit LogStringUint("modifierTest1 a:",a);
a = 5;
emit LogStringUint("modifierTest2 a:",a);
return a;
}
}
执行的日志:
[ { “from”: “0xDf9D0C45d97f134151a386E0AA23b09CA903c13f”, “topic”: “0xfb812593d6d1d6fbf5e981f61205a76acc217dd9dcc7b95c4f58247fb09f2658”, “event”: “LogStringUint”, “args”: { “0”: “modifierA before a=:”, “1”: “0”, “id”: “modifierA before a=:”, “data”: “0” } }, { “from”: “0xDf9D0C45d97f134151a386E0AA23b09CA903c13f”, “topic”: “0xfb812593d6d1d6fbf5e981f61205a76acc217dd9dcc7b95c4f58247fb09f2658”, “event”: “LogStringUint”, “args”: { “0”: “modifierA before arga=:”, “1”: “10”, “id”: “modifierA before arga=:”, “data”: “10” } }, { “from”: “0xDf9D0C45d97f134151a386E0AA23b09CA903c13f”, “topic”: “0xfb812593d6d1d6fbf5e981f61205a76acc217dd9dcc7b95c4f58247fb09f2658”, “event”: “LogStringUint”, “args”: { “0”: “modifierB before a=:”, “1”: “0”, “id”: “modifierB before a=:”, “data”: “0” } }, { “from”: “0xDf9D0C45d97f134151a386E0AA23b09CA903c13f”, “topic”: “0xfb812593d6d1d6fbf5e981f61205a76acc217dd9dcc7b95c4f58247fb09f2658”, “event”: “LogStringUint”, “args”: { “0”: “modifierC before a=:”, “1”: “0”, “id”: “modifierC before a=:”, “data”: “0” } }, { “from”: “0xDf9D0C45d97f134151a386E0AA23b09CA903c13f”, “topic”: “0xfb812593d6d1d6fbf5e981f61205a76acc217dd9dcc7b95c4f58247fb09f2658”, “event”: “LogStringUint”, “args”: { “0”: “modifierB after a=:”, “1”: “0”, “id”: “modifierB after a=:”, “data”: “0” } }, { “from”: “0xDf9D0C45d97f134151a386E0AA23b09CA903c13f”, “topic”: “0xfb812593d6d1d6fbf5e981f61205a76acc217dd9dcc7b95c4f58247fb09f2658”, “event”: “LogStringUint”, “args”: { “0”: “modifierA after a=:”, “1”: “0”, “id”: “modifierA after a=:”, “data”: “0” } }, { “from”: “0xDf9D0C45d97f134151a386E0AA23b09CA903c13f”, “topic”: “0xfb812593d6d1d6fbf5e981f61205a76acc217dd9dcc7b95c4f58247fb09f2658”, “event”: “LogStringUint”, “args”: { “0”: “modifierA after arga=:”, “1”: “10”, “id”: “modifierA after arga=:”, “data”: “10” } } ]
4合约继承
solodity合约的继承是使用关键字is 当一个合约继承另一个合约时,只会部署一个合约,子合约会复制父合约的代码 构造函数的参数传递可以在继承时传递 在继承时传递或者通过构造器在构造函数时传递
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.4.0;
contract LevelOne {
uint8 num;
constructor(uint8 numArg) public{
num = numArg;
}
event LogUint8(string,uint8);
function printNum() public {
emit LogUint8("LevelOne",num);
}
}
//继承时传递参数
contract LevelTwoOne is LevelOne(2){
//重写父合约的方法
function printNum() public {
emit LogUint8("LevelTwoOne",num);
}
}
//构造函数中传递参数
contract LevelTwoTwo is LevelOne{
constructor() LevelOne(31) public{
}
//重写父合约的方法
function printNum() public {
emit LogUint8("LevelTwoTwo",num);
}
}
//在构造函数中传递参数,调用父合约的PrintNum方法
contract LevelTwoThree is LevelOne{
constructor() LevelOne(31) public{
}
}
LevelTwoOne 部署日志:
[ { “from”: “0xa42b1378D1A84b153eB3e3838aE62870A67a40EA”, “topic”: “0xa9b4ee9b4192341ff2079d8152ddc2cdebd57d8a9df0609fa13cff40ef51d1f7”, “event”: “LogUint8”, “args”: { “0”: “LevelTwoOne”, “1”: 2 } } ]
LevelTwoTwo 部署日志:
[ { “from”: “0x3cA38E089Cd3BF3cF24Dabc40dF0c988075b2729”, “topic”: “0xa9b4ee9b4192341ff2079d8152ddc2cdebd57d8a9df0609fa13cff40ef51d1f7”, “event”: “LogUint8”, “args”: { “0”: “LevelTwoTwo”, “1”: 31 } } ]
LevelTwoThree 部署日志:
[ { “from”: “0xa6165bbb69f7e8f3d960220B5F28e990ea5F630D”, “topic”: “0xa9b4ee9b4192341ff2079d8152ddc2cdebd57d8a9df0609fa13cff40ef51d1f7”, “event”: “LogUint8”, “args”: { “0”: “LevelOne”, “1”: 31 } } ]
函数继承多个函数时,后面的函数会重写前面的函数方法 super关键字,当使用super时,调用的是继承的该函数
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.4.0;
contract LevelOneOne{
event LogString(string);
function printNum() public {
emit LogString("LevelOneOne");
}
}
contract LevelOneTwo{
event LogString(string);
function printNum() public {
emit LogString("LevelOneTwo");
}
}
//同时继承多个合约时,后面的合约会重写前面合约的同名方法
contract LevelTwoOne is LevelOneOne,LevelOneTwo{
function printNum() public {
emit LogString("LevelTwoone");
}
//super关键字标识 方法为继承的方法
//同时继承多个合约时,后面的合约会重写前面合约的同名方法
function testSuper() public {
super.printNum();
}
}
contract LevelTwoTwo is LevelOneTwo,LevelOneOne{
//override function
function printNum() public {
emit LogString("LevelTwoTwo");
}
//super- function is contract-inherited 's function
function testSuper() public {
super.printNum();
}
}
LevelTwoOne部署日志 printNum()日志:
[ { “from”: “0xC1144C9dbf6F3489CE9C808a1Da653FB4465403d”, “topic”: “0xa95e6e2a182411e7a6f9ed114a85c3761d87f9b8f453d842c71235aa64fff99f”, “event”: “LogString”, “args”: { “0”: “LevelTwoone” } } ]
testSuper()日志:
[ { “from”: “0xC1144C9dbf6F3489CE9C808a1Da653FB4465403d”, “topic”: “0xa95e6e2a182411e7a6f9ed114a85c3761d87f9b8f453d842c71235aa64fff99f”, “event”: “LogString”, “args”: { “0”: “LevelOneTwo” } } ]
LevelTwoTwo部署日志 printNum()日志:
[ { “from”: “0xC8CF29d9D1595a3588AD36E6349A0E9a5b632720”, “topic”: “0xa95e6e2a182411e7a6f9ed114a85c3761d87f9b8f453d842c71235aa64fff99f”, “event”: “LogString”, “args”: { “0”: “LevelTwoTwo” } } ]
testSuper()日志:
[ { “from”: “0xC8CF29d9D1595a3588AD36E6349A0E9a5b632720”, “topic”: “0xa95e6e2a182411e7a6f9ed114a85c3761d87f9b8f453d842c71235aa64fff99f”, “event”: “LogString”, “args”: { “0”: “LevelOneOne” } } ]
抽象合约 如果一个合约中至少有一个方法没有被实现,那么这个合约就是抽象合约 抽象合约无法被编译部署 一个合约继承了抽象合约但是没有实现抽象合约内方法的话,也会被定义为抽象合约 抽象合约中有几个为实现的方法,继承的子合约中就需要实现几个方法
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.4.0;
/**
* 抽象合约
*/
contract CalcContract{
constructor()public{
}
//定义抽象方法
function calc(uint8 a,uint8 b) public view returns(uint8);
// function minus(uint8 a,uint8 b) public returns(uint8);
function mul(uint a,uint b)public pure returns(uint){
return a*b;
}
}
// /**
// * 继承抽象合约
// */
contract Add is CalcContract{
//实现抽象方法
function calc(uint8 a,uint8 b) public view returns(uint8){
return a + b;
}
function getBalance()public view returns(address addr,uint256 num){
//return(msg.sender,msg.sender.balance);
return(address(this),address(this).balance);
}
}
接口合约 接口合约和抽象合约类似 定义合约方法 用于子合约继承实现 接口合约比较严格 不能继承其它合约和接口 不能定义构造函数 不能定义结构体 不能定义变量 不能定义枚举 可以定义事件,通过emit抛出
5 solidity库
通过library 关键字标识solidity库 库的调用方式是delegatecall方式
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.4.0;
library TestLibrary{
struct Student{
uint32 id;
string name;
}
function add(uint8 a,uint8 b) public pure returns(uint8) {
return a + b;
}
}
contract RefLibrary{
TestLibrary.Student tim = TestLibrary.Student(1,"tim");
function getTim() public view returns(uint32,string){
return(tim.id,tim.name);
}
function callAdd(uint8 a,uint8 b) public pure returns(uint8){
return TestLibrary.add(a,b);
}
}
引入solidity库 通过import引入目录下的sol合约
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.4.0;
import "./Calc.sol";
import "./AddLib.sol";
contract AddCalc is Calc{
function calc (uint a,uint b)public pure returns(uint){
return AddLib.add(a,b);
}
}
using…for 让库函数attach到指定的类型上,指定的类型具备库函数的方法
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.4.0;
library CalcLib {
function add(uint8 a, uint8 b) public pure returns(uint8){
return a + b;
}
function sub(uint8 a, uint8 b) public pure returns(uint8){
return a - b;
}
}
contract UsingForContract {
using CalcLib for uint8;
function add(uint8 a,uint8 b) public pure returns(uint8) {
return a.add(b);
}
function sub(uint8 a,uint8 b) public pure returns(uint8) {
return a.sub(b);
}
}
// // SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.4.0;
library CalcLib{
function sum(uint8[] storage arrs)public view returns(uint8){
uint8 totls=0;
for(uint8 i=0;i<arrs.length;i++){
totls+=arrs[i];
}
return totls;
}
}
contract usingForContract{
using CalcLib for uint8[];
uint8[] array=[2,3,4,5];
function sum()public view returns(uint8){
return array.sum();
}
}
6 solc编译合约
solc -o build --combined-json abi,bin getNum.sol -o参数表示输出目录的名称 --combined-json以json的形式组合输出后面的问价 abi接口 bin十六进制输出合约的字节码 solc -o build --abi --bin getNum.sol
合约中有导入库的情况时: 在编译合约时需要设置导入合约的文件前缀 solc /=/ Hello.sol 举例 import “/lib/sub/Sub.sol” solc /lib/sub=/lib/sub Hello.sol
|