solidity智能合约

ifcoder · · 3555 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

[toc]

solidity智能合约入门基础

区块链的价值

信任

创建永久的、安全的、不可篡改的可追溯的记录

价值

独一无二的资产转移,不需要第三方的组织

可靠

分布式、稳定性

solidity 语言

image

代码结构

solidity在线编辑器

pragma solidity ^0.4.23;

contract HelloWorld{
    string public name = "DNA";
    
    function getName() public view returns(string){
        return name;
    }
    function changeName(string _name) public{
        name = _name;
    }
}


image

基本solidity变量类型

  • bool:true、false
  • string:"sdfsdfsd"
  • int:0,999,-999
  • byte:0x12,0xab

整型变量

int:有符号,int8,int16,int24,int32...int256

uint:无符号,uint8,uint16,uint24,uint32...uint256

tips:在solidity变量声明时,int默认声明int256,uint默认声明uint256

bool逻辑操作

image

函数

image

函数类型

view: 读取区块链上的数据,但是不修改区块链上面的数据

pure:不修改也不读取区块链上面的数据

image
function pingfang(uint a,uint b) public pure returns(uint){
        return a**b;//a**b,表示a的b次方 
    }

一笔事务的控制台信息

调用上面的changeName(string _name)函数,控制台信息如下:

image

transaction cost:事物总共花费,包括execution cost

execution cost:计算花费

input:

image

位运算

&(与):全都是1才为1,不然全是0

|(或):全是0为0,有一个1就是1

^(异或):相等为0,不等为1

~(取反):0变1,1变0

十进制3用二进制表示为:00000011
              3<<1:00000110
              3>>1:00000001
image

byte类型

位是计算机中存储数据最小的单位,只能存储0或1

image

用位操作数据比较麻烦,我们把位封装为byte

image
  • bytes1代表1个字节,bytes2代表2个字节,bytes32代表32个字节
  • bytes32表示的大小等同于int256(int256中的256表示256位)
image

十进制3用16进制表示为0x03,一位16进制==4位二进制

image
image

固定长度字节数组的代码

pragma solidity ^0.4.20;
contract bytesTest{
    bytes2 a = 0x6a5c;//定义固定长度字节数组
    
    function changeData()public {
        a[0]=0x00; //编译器会报错:TypeError:Expression has to be an lvalue
        //意思是a[0]必须是可以修改的左值
    }
    
     function changeLength(){
        a.length=a.length+1;//编译器会报错:TypeError:Expression has to be an lvalue
        //意思是a.length不可以被修改
    }
}

==总结:固定长度的字节数组的数据和长度都不能被更改==

动态长度字节数组的代码

pragma solidity ^0.4.20;
contract bytesTest{
   bytes a=new bytes(2);
   
   function initValue()public{
       a[0]=0x6a;
       a[1]=0x7b;
   }
   function getLength()public view returns(uint){
       return a.length;
   }
   
   function getValue()public view returns(bytes){
       return a;
   }
   function changeData()public returns(bytes){
       a[0]=0x12;
       return a;
   }
   function changeLength()public{
       a.length+=1;
   }
   function pushBytes()public{
       a.push(0x11);
   }
   
}

总结:

  1. 动态长度字节数组的数据和长度==可以被更改==,增加长度在数据==末尾补零==,减少长度直接==截取数据==(从低位开始截取,右边低位,左边高位)。
  2. 动态字节数组的特有方法push(),直接在数据末尾添加数据

固定长度字节转换

contract fixToDynamic{
    bytes6 name=0x6f56e5e7e4e3;
    
    function toDynamic()public view returns(bytes){
        bytes memory rtn = new bytes(name.length);
        
        for (uint i = 0;i<name.length;i++){
            rtn[i]=name[i];
        }
        return rtn;
    }
}

tips:循环中索引的变量声明一定要是uint

字符串-特殊的动态字节数组

想要操作字符串需要把字符串转化为动态长度字节数组

字符串转动态字节数组,直接强制类型转换即可:bytes(str)

字符串==无法通过==以下方式获得字符串的长度,和指定索引下的数据

contract StringTest{
    string  public name = "DNA";
    
   function getLength()public returns(uint){
       return name.length; //无法通过此种方式获取字符串长度
   }
   
   function getData()public returns(bytes1){
       return name[0];  //无法通过此种方式获取指定索引处的字符
   }
}

把字符串转化为动态长度字节数组后继续操作

contract StringTest{
    string  public name = "DNA";
    
   function getLength()public view returns(uint){
       return bytes(name).length; 
   }
   
   function getData()public returns(bytes1){
       return bytes(name)[0];
   }
}

在函数内声明动态长度字节数组时,需要加上memory关键字:bytes memory newNane=new bytes(name.length)

关于memorystorage,我们后续详细讲解

动态长度字节数组转化为字符串

可以直接将动态长度字节数组强制类型转化为字符串string(dynamicArr)

pragma solidity ^0.4.20;
contract DynamicToString{
   bytes a=new bytes(2);
   function initValue()public{
       a[0]=0x6a;
       a[1]=0x6f;
   }
   function dynamicToStr()public view returns(string){
       return string(a);
   }
}

固定长度字节数组转化为字符串

==不能像动态长度字节数组一样,直接强制类型转化为字符串==。而需要把固定数组转为动态数组,再将动态数组转化为字符串。

pragma solidity ^0.4.20;
contract FixToString{
   bytes2 name=0x6a6f;
   function fixToStr(bytes32 _data)public pure returns(string){
       uint count = 0;
       
       for(uint i=0;i<_data.length;i++){
           if(_data[i]!=0){
               count++;
           }
       }
       bytes memory rtn = new bytes(count);
       for(uint j=0;j<rtn.length;j++){
           rtn[j]=_data[j];
       }
       
       
       return string(rtn);
   }
}

固定长度数组

contract FixArray{
    uint[5] public grade=[1,2,3,4,5];
    int[5] math;
    bytes2[3] bytesTest;
    function init()public{
        bytesTest[0]=0x6af6;
    }
    function getArray()public view returns(bytes2[3]){
        return bytesTest;
    }
    function init2()public{
        grade[0]=100;
        grade[1]=200;
    }

    function getArray2()public view returns(uint[5]){
        return grade;
    }
    function getLength()public view returns(uint){
        return grade.length;
    }
}

注意:因为是固定长度数组,故不能够更改数组长度,也不能通过array.push()增加数据

动态长度数组

pragma solidity ^0.4.20;
contract DynamicArray{
    uint[] grade=[1,2,3,4,5];
    function getArray()public view returns(uint[]){
        return grade;
    }
    function getLength()public view returns(uint){
        return grade.length;
    }
//先调用changeLength1(),后调用changeLength2(),最后调用pushData()
    
    function changeLength1()public {
        grade.length=3; //grade[1,2,3,4,5]->grade[1,2,3]
    }
    function changeLength2()public {
        grade.length=5; //grade[1,2,3]->grade[1,2,3,0,0]
    }
    function pushData()public{
        grade.push(99);//grade[1,2,3,0,0]->grade[1,2,3,0,0,99]
    }
    
}

二维数组

solidity中二维数组的定义和其他语言的不一样,比如golang中

arr:=[2][3]int{{1,1,1},{2,2,2}}

而solidity中,把二维的长度放在前面,一维的长度放在后面

uint[3][2] arr=[[1,2,3],[1,2,3]];

但solidity中二维数组长度的获取却和golang一样,arr.length表示的一维的长度,而arr[0].length表示的二维的长度

solidity获取二维数组元素的方式和规则与golang一样,arr[0][0]表示arr数组中第一维的第一个元素

contract twoArray{
    uint[3][2] arr=[[1,2,3],[1,2,3]];
    function getArr()public view returns(uint[3][2]){
        return arr;
    }
    function getLength1()public view returns(uint){
        return arr.length;//返回2
    }
    function getLength2()public view returns(uint){
        return arr[0].length;//返回3
    }
    function getLength3()public view returns(uint){
        return arr[1].length;//返回3
    }
    function changeData()public returns(uint[3][2]){
        arr[0][1]=8023;//uint256[3][2]: 1,8023,3,1,2,3
        return arr;
    }
}
    
}

注意:二维数组不能够修改长度,也不能像动态数组一样push()

动态二维数组

pragma solidity ^0.4.20;
contract dynamicTwoArray{
    uint[][] arr=[[1,2,3],[4,5,6]];
    // function getArr()public view returns(uint[][]){
    //     return arr;
    // }
    function getLength1()public view returns(uint){
        return arr.length;
    }
    function getLength2()public view returns(uint){
        return arr[0].length;
    }
    
    function changeLength()public {
        arr.length=3;// 可以改变一维数组的长度,但是没有获取扩容后的数据
    }   
     function changeLength2()public {
        arr[0].length=4;//可以改变二维数组的长度,并且扩容的数据为类型默认值
    }   
    function getData(uint a,uint b)public view returns(uint){
        return arr[a][b];
    }
    function pushData()public{
        arr.push([7,8,9,10]);//可以向动态一维数组后面push数据
    }
    function pushData2()public{
        arr[0].push(56);//可以动态的像二维数组后面push数据
    }
}

==注意:动态二维数组不能作为函数返回值==

数组字面量

contract ArrayLiteratrals{
    function getArrayLiteratel1()public pure returns(uint[3]){
       return [1,2,3];//会报错,提示不能用uint8的数组作为uint数组的返回值
   }
    function getArrayLiteratel1()public pure returns(uint[3]){
       return [1,256,3];//会报错,提示不能用uint16的数组作为uint数组的返回值
   }
   function getArrayLiteratel()public pure returns(uint[3]){
       return [1,2,uint(3)];//解决了上述问题
   }
}

地址类型

类型大小:160位二进制,40位16进制

pragma solidity ^0.4.20;
contract addressTest{
//   0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c->1154414090619811796818182302139415280051214250812
//   0x583031D1113aD414F02576BD6afaBfb302140225->503465963245955021447394431766750955009431831077
    address public account1 = 0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c;
    address public account2 = 0x583031D1113aD414F02576BD6afaBfb302140225;
    
    function changeIt()public view returns(uint160){
        return uint160(account1);//1154414090619811796818182302139415280051214250812
    }
    function changeIt2()public pure returns(address){
        return address(1154414090619811796818182302139415280051214250812);//0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c
    }
    function check1()view public returns(bool){
        return account1>account2;//true
    }
}

转账操作

如何让外部账户的钱,转到合约账户当中?

pragma solidity ^0.4.20;
contract payableTest{
//外部账户调用 pay()并且设置value值的时候,可以将钱转到合约账户中
    function pay()public payable{
        
    }
    function getBanlance()public view returns(uint){
        return address(this).balance;
    }
    function getThis()public view returns(address){
        return this;//本合约的地址:0x9240dDc345D7084cC775EB65F91f7194DbBb48d8
    }
    //获取外部账户余额 
    function getExternalBalance(address account)public view returns(uint){
        return account.balance;
    }
    //给指定地址(account)转账 
    function transfer1()public payable{
        address account=0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db;
        account.transfer(msg.value);
    }
    //向当前合约账户转账 
    function transfer2() public payable{
        address(this).transfer(msg.value);
    }
    //向指定账户转钱
     function transfer3() public payable{
        address(0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db).transfer(10*10**18);
    }
    function () public payable{
        
    }
}

全局属性

pragma solidity ^0.4.20;
contract golobal{
// block.coinbase (address): :当前块的矿工的地址
// block.difficulty (uint):当前块的难度系数
// block.gaslimit (uint):当前块汽油限量
// block.number (uint):当前块编号
// block.blockhash (function(uint) returns (bytes32)):指定块的哈希值——最新的256个块的哈希值
// block.timestamp (uint):当前块的时间戳
// msg.data (bytes):完整的calldata
// msg.gas (uint):剩余的汽油
// msg.sender (address):消息的发送方(当前调用)
// msg.sig (bytes4):calldata的前四个字节(即函数标识符)
// msg.value (uint):所发送的消息中wei的数量
// now (uint):当前块时间戳(block.timestamp的别名)
// tx.gasprice (uint):交易的汽油价格
// tx.origin (address):交易发送方(完整的调用链)
    function getGlobal1()public view returns(address){
        return block.coinbase;
    }
    
}

转账的三种方式

address.transfer()

contract transferDepp{
    function transfer1(address _address)public payable{
        _address.transfer(msg.value);
    }
    function transfer2(address _address)public payable{
        _address.transfer(10*10**18);
    }
    function transfer3(address _address)public payable{
        _address.transfer(10 ether);
    }
    
}

address.send()

contract transferDepp{
//返回值true表示转账成功,否则失败
    function transfer4(address _address)public payable returns(bool){
        return _address.send(10 ether);
    }
}

注意:send()和transfer()的区别是在于,函数调用者的付款金额如果小于合约中函数的转账金额,transfer会直接执行失败,全部回滚。而send会执行成功,直接扣除函数调用者的以太币,不转账给收款方,而是当累计付款金额达到标准后再一并转账给收款方。

address.call.value().gas()()

本方法不能避免重入攻击;transfer和send都可以避免重入攻击

function transfer5(address _address)public payable returns(bool){
        return _address.call.value(10 ether)();
    }

总结:

  1. transfer与send相似,都是转账操作
  2. transfer出错抛出异常,全都回滚
  3. send出错不抛出异常,返回true或false
  4. send无论出错与否,前后代码依旧执行,return后面的代码不执行
  5. tranfer相对于send更安全

mapping映射

定义方式:

mapping(类型 => 类型) 变量名

简单模拟网站注册:

pragma solidity ^0.4.20;
contract mappingTest{
   uint id=0;
   mapping(address=>uint) idmap;
   mapping(uint=>string) namemap;
   
   function regist(string name) public{
       address account=msg.sender;
       id++;
       idmap[account]=id;
       namemap[id]=name;
   }
   function getIDByAddress(address _address)public view returns(uint){
       return idmap[_address];
   }
    function getNameByID(uint _id)public view returns(string){
       return namemap[_id];
   }
    
}

函数与函数重载

函数的定义

function 函数名(){private|public|external|internal}[pure|view|payable|const][returns(<type>)]
image

函数的重载

pragma solidity ^0.4.20;
contract mappingTest{

    
    uint public num = 2;
    
    function change() public{
        num = 10;//思考问题:区块链上的数据不可更改,但为什么如此操作可以成功执行
    }
    
    
    //将bytes6转化为动态字节数组
  function  Todynamic(bytes6 name) pure public returns(bytes){
        
        bytes memory newName = new bytes(name.length);
        
        for(uint i = 0;i<name.length;i++){
           newName[i] =  name[i];
        }
        return newName;
    }
    
    
    //复用Todynamic()函数
    bytes public  myname;
    function  getlength(bytes6 name)  public returns(uint){
       myname =  Todynamic(name);
       return myname.length;
    }
    
    // 如下两个函数无法通过编译
    // function  fun(){
        
    // }
    
    //  function  fun(){
        
    // }
    
    // 如下四个函数无法通过编译
    // function  fun() returns(uint){
        
    // }
    
    //  function  fun() returns(bytes1){
        
    // }
    
    //   function  fun() returns(uint){
        
    // }
    
    //  function  fun(){
        
    // }
    
    // 如下两个函数可以通过编译
    function  fun(uint k) public pure{
        
    }
    
     function  fun() public pure{
        
    }
    
    //如下两个函数可以通过编译
       function  fun2(uint a) public pure{
        
    }
    
     function  fun2(bytes1 b) public pure{
        
    }
    
     //如下两个函数可以通过编译
    function  fun3(uint a) public {
        num = 256;
    }
    
     function  fun3(uint8 b) public{
         num = 8;
    }

       function test() public{
        //fun3(2); //不能通过编译
        //fun3(uint8(2)); //不能通过编译
        //fun3(uint256(2)); //通过编译
        fun3(256);  //通过编译
    }
 
    // 如下两个函数可以通过编译
       function  fun4(address a) public {
        num = 256;
    }
    
     function  fun4(uint160 b) public{
         num = 8;
    }
    
    
    
    function  test3() public{
       fun4(address(0x14723a09acff6d2a60dcdf7aa4aff308fddc160c));  //通过编译
       //fun4(0x14723a09acff6d2a60dcdf7aa4aff308fddc160c);   //不能通过编译
       //fun4(uint160(0x14723a09acff6d2a60dcdf7aa4aff308fddc160c));    //不能通过编译
    }
      function  test4() public{
       fun4(address(123));
    }

}

重载的条件:

  1. 函数的返回值是没有办法区别函数的
  2. 参数的个数可以区别函数
  3. 参数的类型一定程度上可以区别函数,详情看上面代码示例

函数参数

pragma solidity ^0.4.20;



contract funcParam{
    
    uint public num;
    string public name;
    
    function setparam(uint _num,string _name) public {
        num  = _num;
        name = _name;
    }
    
    function Test() public {
        setparam(99,"bob");
    }
    
      function Test2() public {
        setparam({_num:99,_name:"bob"});
    }
      function Test3() public {
        setparam({_name:"Alice",_num:999});
    }
    
    // 如下函数无法通过编译
    // function Test4() public {
    //     setparam(100);
    // }
    
}

tips:编译通过后,如果在deploy contracts上面传递的参数不全时,会给未传递的参数自动传递类型默认值

函数返回值

pragma solidity ^0.4.20;

contract returnValue{
   
   function  resValue() pure public returns(uint){
       uint a = 10;
       return a;
   }
   
 function  recieveValue() pure public returns(uint){
       uint b;
       b = resValue();
       return b;
   }
   
   
   function resValue2() pure public returns(uint num1){
       num1 = 100;
   }
   
     function resValue3() pure public returns(uint num1){
       num1 = 100;
       return 99;   // 返回99
   }
   
     function resValue4() pure public returns(uint num1){
       uint b = 88; // 返回值是0
   }
   
      function mulvalue(uint a,uint b) pure public returns(uint,uint){
        uint add =  a+b;
        uint mul = a*b;
        return (add,mul);
   }
   
       function mulvalue2(uint a,uint b) pure public returns(uint add,uint mul){
         add =  a+b;
         mul = a*b;
   }
   
   
   function reverse(uint a,uint b) returns(uint ,uint){
       
       return (b,a);
   }
   
   uint public  resA = 0;
   uint public resB = 0;
   
     function reverse2(uint a,uint b) {
       (resA,resB) = reverse(a,b);
   }
   
   
   
}

tips:多返回值要用“()”括起来

变量作用域

pragma solidity ^0.4.20;

contract scope{
    
    uint public a = 100;
    uint b = 200;
   //  uint public a = 999;
    
    function scopeTest() pure  public returns(uint){
        uint  a = 88;
        
        a = 77;
        return a;   // 返回值是77
    }
    
     function scopeTest2(uint a) pure  public returns(uint){
      
     // uint a =  0;    //编译失败
     
    //  for(uint a = 0;a<8;a++){    // 编译失败
         
    //  }
    
    {
        // uint a = 0;  //编译失败
          a = 99;
        return a ;
    }
    
    

    }
    
    
}

值拷贝

pragma solidity ^0.4.23;


contract valueCopy{
    
    uint public a = 100;
    
    uint public b = a ;
    
    function change() public {
        b = 999;//此操作不会改变a的值
    }
    
    
     function change2() public  pure returns(uint,uint){
       uint a1 = 100;
       uint b1 = a1;
       b1 =  999;
       return (a1,b1);//返回100,999
    }
    
    
    function change3(uint num) public returns(uint){
        num++;
        return num;
    }
    
    function test() returns(uint){
        
       uint result  =  change3(a);
    
        return result;  //不会改变a的值,返回101
    }
    
}

构造函数

构造函数名字要与合约名字完全一样(==已过时==),现在构造函数用constructor来定义

pragma solidity ^0.4.20;

contract  ontractinit{
    
    uint public a ;
    
    // function ontractinit() public{
    //     a = 100;
    // }
    
    //   function ontractinit(uint _a,uint _b) public{
    //     a = _a;
    // }
    
    address public owner;
    // constructor(uint _a) public{
    //     a = _a;
    // }
    constructor() public {
        owner = msg.sender;
    }
    
}

modifier

modifier的定义

image

modifier的执行逻辑

image
image

modifier示例代码

pragma solidity ^0.4.23;

contract  modifire{
   address  owner;
   uint public a  = 0;
   constructor() public{
       owner = msg.sender;
   }
   
   modifier OnlyOwner(){
    require(msg.sender==owner);//需要函数调用者和owner相同才可以执行后面的函数逻辑
       _;
   }
   
   function changeIt(uint _num) public OnlyOwner{
       a = _num;
   }
    
    
     function getIt() view public OnlyOwner returns(address) {
      return owner;
   }
}
image
image
pragma solidity ^0.4.23;
 

contract  modifiererParam{
    uint public level = 9;
    string public name;
    uint public DNA;
    
  // 带参数的modifier
    modifier controlLevel(uint _needlevel){
        require(level>_needlevel);
        _;
    }
    
    function changeName(string _name) public controlLevel(2){
        name = _name;
    }
    
    
      function changeDNA(uint _dna) public controlLevel(10){
        DNA = _dna;
    }
}


contract  mulmodifiererDeep{
    uint public a = 0;
    
    modifier mod1{
        a = 1;
        _;
        a = 2;
    }
    
    function test() public  mod1{
        
        a = 100;    // 最终a的值为2

    }
 
}


contract  mulmodifiererDeep2{
    uint public a = 0;
    
    modifier mod1{
        a = 1;
        _;
        a = 2;
    }
    
     modifier mod2{
        a = 3;
        _;
        a = 4;
    }
    
    function test() public  mod1 mod2{
        a = 100;    // a的值为2 
    }
 
}

contract  mulmodifiererDeep3{
    uint public a = 0;
    
    modifier mod1{
        a = 1;
        _;//  a = 100;
        a = 2;
    }
    
     modifier mod2{
        a = 3;
        _;
        a = 4;
    }
    
    function test() public   mod2 mod1{
        a = 100;    // a的值为4
    }
}

const与继承

5.0版本后,constant关键字就被废除了

继承通过关键字is实现


 pragma solidity 0.4.23;


contract constantTest{
    
    
    uint public constant num = 100;
     int public constant num2 = 100;
        bytes32 public constant num3 = 0x54;
        string public constant num4 = "jonson";
        //bytes
       //  bytes32[] public constant num5;
    function test() public pure returns(uint){
        return num;
    }
    
    function change() public {
       // num = 99;
    }
}


contract father{
    
    uint public money  =10000;

    function dahan() public returns(string){
        return "dahan";
    }
}

contract son is father{
    uint public girlfriend;
    function change() public{
        money = 99;
    }
}

可见性

function 函数名(){private|public|external|internal}[pure|view|payable|const][returns(<type>)]

public:任何人都可以调用该函数,包括Dapp的使用者。

private:只有合约本身可以调用该函数(在另一个函数中)。

internal:只有这份合同以及由此产生的所有合同才能称之为合同。
external:只有外部可以调用该函数,而合约内部不能调用。

状态变量的可见性

  1. 没有external属性
  2. public、internal 或者不加任何修饰符,都可以被继承
  3. private不能被继承

函数的可见性

  1. public:可以被继承,子类含有父类方法,可以被子类任意调用

 pragma solidity 0.4.23;


contract father{
    uint public  money =10000;
    function dahan()public pure returns(string){
        return "dahan";
    }
}

contract son is father {
    function getMoney() public view returns(uint){
        return money;
    }
    function test()public pure returns(string){
        return dahan();
    }
}
  1. external:可以被继承,子类含有父类方法,但子类调用必须通过this.继承的方法();来定用

 pragma solidity 0.4.23;


contract father{
    uint public  money =10000;
    function dahan()external pure returns(string){
        return "dahan";
    }
}

contract son is father {
    function getMoney() public view returns(uint){
        return money;
    }
    function test()public view returns(string){
        return this.dahan();
    }
}
  1. internal:可以被继承,但子类不含有父类方法(不能内部直接调用),但是可以调用父类的方法(可以通过外部调用,如下面的test()方法)
 pragma solidity 0.4.23;


contract father{
    uint public  money =10000;
    function dahan()internal pure returns(string){
        return "dahan";
    }
}

contract son is father {
    function getMoney() public view returns(uint){
        return money;
    }
    function test()public pure returns(string){
        return dahan();
    }
}
  1. private:不能被任何人所继承

可以在一份合约中实例化另一份合约

pragma solidity ^0.4.23;

contract father{
    uint  money  = 10000;
    
    function dahan() external pure returns(string){
        return "dahan";
    }
}

contract son is father{
    function getMoney() public view returns(uint){
       return money;
    }
    
    function test() public view returns(string){
        return this.dahan();
    }
    
}

contract testExternal{
   father f = new father();
   
     function test() public view returns(string){
        return f.dahan();
    }
   
    
}

getter函数

给变量添加public修饰符,如果自己不手写一个get函数的话,系统会自动生成一个get函数(下面的num()),如果自己手写了,则系统不再继续生成

contract getter{
  uint public num = 100;
  
   function num() external pure returns(uint){
       return 200;
   }
}

因为系统自动生成的get函数是external的,所以调用的时候必须通过this.num()调用

contract getter{
  uint public num = 100;
  
 function test(){
     this.num();
    
 }

mapping的getter函数

pragma solidity ^0.4.23;


contract getter{

mapping(uint =>string) public map;
//系统自动生成的getter函数如下
function map(uint key) external view returns(string){
    return map[key];
}

function change() public{
    
    map[2] = "jonson";
}

}

因为系统自动生成的get函数是external的,所以调用的时候必须通过this.map()调用

function test() returns(string){
   return  this.map(2);
    
}
contract getter{

mapping(uint=>mapping(uint=>uint)) public grademap;

function changegrade() public{
    grademap[1][21] = 99;
      grademap[2][21] = 56;
}

}

多继承

多继承中相同变量以is后面的父类为标准(函数也如此)

  1. son的money为8888
contract father{
    uint public money=9999;
}
contract mother{
    uint public money=8888;
}
contract son is father,mother{
    //son的money为8888
}
  1. son的money为9999
contract father{
    uint public money=9999;
}
contract mother{
    uint public money=8888;
}
contract son is mother,father{
    //son的money为9999
}

contract father{
    uint public money=9999;
    function dahan()public pure returns(string){
        return "father";
    }
}
contract mother{
    uint public money=8888;
    function dahan()public pure returns(string){
        return "mother";
    }
}
contract son is father,mother{
        function dahan()public pure returns(string){
        return father.dahan();//返回结果是"father"
    }
}

总结:

  1. 支持函数重写
  2. 变量的使用遵循“就近原则”,也就是说,子类定义了和父类同名的变量时,子类中使用的变量就是子类中定义的
  3. 不能通过super.money访问父类变量
  4. 可以通过super.dahan()访问父类方法,但不能super.super.dahan()访问父类的父类的方法
  5. 可以通过父类合约的名字.方法()来访问父类的方法

EVM数据存储

storage与memory

  • storage:状态变量(全局变量)会存储在这里。永久性存储数据(因为会把数据写入区块链当中),

  • stack:函数中的本地变量(局部变量)默认会存储在这里,暂时性存储数据。

  • memory:暂时性存储数据,实参(函数中实际传递的参数)默认会存储在这里。

tips:storage和memory都需要消耗gas,但是storage更贵。

代码1:

pragma solidity ^0.4.23;

contract storageAndMemory{
    uint a = 5;//storage变量 
    
    function changeIt() public{
        a=1000;//stack变量
    }
    
    function add(uint num)public pure returns(uint){
        num+=1; //因为num是作为参数传进来的,所以是memory变量
        return num;
    }
    function test()public pure returns(uint,uint){
        uint i =2;  // stack变量
        uint j=add(i);// stack变量
        return (i,j);
    }
}
image

代码2:
==动态数组在函数内部定义时,默认声明为storage==


pragma solidity ^0.4.23;

contract storageStart{
    uint[] public arrx;
    
    function test(uint[] arry)public{
        arrx=arry;
        
        uint[] storage Z=arrx;//变量Z存储在stack中,但实际引用的是storage中的数据
        
        Z[0]=100;
        Z.length=10;
    }
    function getLength()public view returns(uint){
        return arrx.length;
    }
}

image

结构体

结构体在函数内部实例化的时候,==实例会存储在memory当中==,而==结构体声明的引用会被默认声明为storage,所以需要将引用显示声明为memory==

contract StructTest{
    
    struct student{
        uint grade;
        string name;
    }
    
    function init()public pure returns(uint,string){
        student memory s= student(100,"jackson");
        //  student memory s1= student({name:"Tommy",grade:99}); 也可以这种方式定义 
        return (s.grade,s.name);
    }
    
}
image

注意:==结构体内部不能嵌套同一结构体==,但==可以嵌套自身的动态数组==。

contract StructTest{
    
    struct student{
        uint grade;
        string name;
        student s;//编译失败
    }
    
}

注意:结构体可以嵌套自身的动态数组

contract StructTest{
    
    struct student{
        uint grade;
        string name;
        student[] s;//通过编译   
    }
    
}

注意:结构体内部可以嵌套另一个结构体

contract StructTest{
    
    struct student{
        uint grade;
        string name;
        
    }
    
    struct student2{
        uint grade;
        string name;
        student s;//通过编译
        
    }
    
}

注意:结构体可以嵌套mapping

contract StructTest{
    
    struct student{
    
        uint grade;
        string name;
        mapping(uint=>student) studentMapping;
    }
    
    
    
}

struct中的mapping

pragma solidity ^0.4.23;


contract StructDeep{
    
    struct student{
    
        uint grade;
        string name;
        mapping(uint=>string) map;// 在定义的时候不用初始化 
        
    }
    student stu;
    function init()public view returns(string){
        
        student memory s=student(100,"jackson"); 
        // s.map[2]="Allen";//memory当中的结构体不能操作内部的mapping对象
        
        stu=s;//将memory当中s存储的student数据复制到storage中,并由stu所指向
        stu.map[2]="Allen";
        
        student storage tempStu = stu;//storage的 tempStu引用stu的数据,不会复制产生新数据 
        tempStu.map[2]="Alice";
        return (stu.map[2]);
    }
    
}

结构体作为函数参数,函数必须是internal类型

 function structParam(student s) internal{
        
    }

在函数内部声明storage变量,并引用状态变量,直接引用storage变量,不会重新复制一份

pragma solidity ^0.4.23;


contract StructDeep{
    
    struct student{
    
        uint grade;
        string name;
        mapping(uint=>string) map;// 在定义的时候不用初始化 
        
    }
    student stu;
    
    
    function structParam(student s) internal{
            student memory guy=s;
    }
     
     function structParam2(student storage s) internal{
            student storage guy=s;
            guy.grade=1000;
           
    }
    function call() public returns(uint){
        structParam2(stu);
        return stu.grade;
    }
}
image
contract memoryTomemory{
    
    struct student{
        uint grade;
        string name;
        
    }
    
    student s1=student(99,"sdf");
    student s2 =s1;//会在storage中复制一份完全相同的数据
    }

memory转storage

pragma solidity ^0.4.23;


contract MemoryTostorage{
    
      struct student{
        uint grade;
        string name;
    }
    
    student stu;
    
    function test(student  memory s) internal{
        
        stu = s;
        s.name = "alice";
    }
    
    
    
    function call() public returns(uint,string){
        
        student memory guy = student(100,"jackson");
        test(guy);//guy作为参数,会进行值拷贝,在memory中复制一份完全相同的数据传进去
        return (stu.grade,stu.name);
    }
    
}
image
image

storage转memory

pragma solidity ^0.4.23;


contract storageToMemory{
    
      struct student{
        uint grade;
        string name;
    }
    
    student stu = student(100,"jackson");
    
function test(student  storage s) internal{
    
    student memory guy = s;//会在memory当中复制一份和相同的一份数据  
    guy.grade = 50;
}

function call() public returns(uint,string){
    
    test(stu);
    
    return (stu.grade,stu.name);
}


    
}
image
image

memory转memory

pragma solidity ^0.4.23;


contract memoryTomemory{
    
    struct student{
        uint grade;
        string name;
        
    }
    
    
    function test(student memory b) internal{
        
        student memory c = b;
        c.name = "jonson";
    }
    
    function call() returns(string){
        student memory a =  student(100,"olaya");
        test(a);
        return a.name;//返回值是joson
    }
    
}

image

枚举


pragma solidity ^0.4.23;


contract enumTest{
    
    enum girl{fengjie,binbin,yuanyuan}//不能加分号 
    
    girl dategirl =girl.fengjie;
    
    
    function getEnum() view returns(girl){
        return dategirl;
    }
    
    function  oneNightDate() returns(string){
        require(dategirl == girl.fengjie);
        dategirl = girl.binbin;
        return "date fengjie";
    }
      function  seconedNightDate() returns(string){
        require(dategirl == girl.binbin);
        dategirl = girl.yuanyuan;
        return "date binbin";
    }
}

有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:ifcoder

查看原文:solidity智能合约

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

3555 次点击  
加入收藏 微博
1 回复  |  直到 2022-01-13 20:17:52
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传