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
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/328172.html
標籤:區塊鏈
