solidity数据类型storage memory calldata modifier前置条件 继承 接口合约 导入库 using...for solc编译

Posted blockchain_yhj

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了solidity数据类型storage memory calldata modifier前置条件 继承 接口合约 导入库 using...for solc编译相关的知识,希望对你有一定的参考价值。

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

以上是关于solidity数据类型storage memory calldata modifier前置条件 继承 接口合约 导入库 using...for solc编译的主要内容,如果未能解决你的问题,请参考以下文章

区块链 以太坊 Solidity状态变量局部变量与memory storage

Solidity Storage底层管理

solidity 安全 未初始化指针的风险——天上不会掉馅饼

Solidity - 内存布局

Solidity极简入门#5. 变量数据存储和作用域

以太坊存储类型(memory,storage)及变量存储详解