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