solidity上课

这篇文章仅用于记录上课笔记!!点击 solidity 查看更多

基本数据类型:

整数,枚举,布尔
Address,contract
Fixed byte array

  • Integer(int,uint)
    uint(unsigned int) 无符号整数,2的n次方减一为最大数值。可以使用type(x).max() 来查看该数据类型的最大值和最小值
    在这里插入图片描述
    部署合约之后可以看到最大值:
    在这里插入图片描述
    可以写一个increment函数来检验:当数据值的范围超出最大值之后,就不会再增加,并且会给出错误信息。
    在这里插入图片描述

  • 枚举类型(Enum type)

  • address
    address含有20个字节长度,address可以转换为uint160和bytes20,表示部署合约的账号地址值。

  • contract合约关键字
    用于声明合约
    在这里插入图片描述

  • 定长字节数组
    使用下标访问,但是不可以使用下标写入数据。
    定义方式:

	bytes1 data;  // 1表示这是一个字节的定长字节数组
	bytes d  = data[0];
  • 引用类型
    数组,struct(结构体),mapping映射
    数组:push 方法 向数组中添加元素
    pop 方法,删除数组当中的一个元素
  • mapping映射
    mapping(key type => value type)
    在这里插入图片描述
    如果要想映射作为函数的参数传入,那么这个函数的可见性必须是internal或者是private.不能是public。
	mapping (string => string) nameToDesc;
    mapping (string => uint8) nameToAge;
    mapping (string => mapping(string=>uint8)) public name2age;
    function setnameToAge (mapping(string=> uint8) storage data) internal{
    
    


    }

contract Storage {
    mapping(address => uint256) balances;
    
    constructor(){
        address deployer = msg.sender;
        balances[deployer] = 100;
    }

    

    function balanceOf(address owner) public view returns(uint256){
        return balances[owner];
    }


    function transfer(address from, address to ,uint256 amount)public{
        
        require(balances[from] >= amount,"The address do not have enough money");
        balances[from] -= amount;
        balances[to] += amount;
	}
 }
  • sd
	// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.2 <0.9.0;

contract ArrayTest{
    uint256[4] public fixedData;
    uint256[] public dynamicData;

    // 定长数组 使用下标来存放数据
    function setFixedData(uint8 index,uint256 value)public{
        fixedData[index] = value;
    }
    // 动态数组 使用push方法来存入变量,不需要使用下标索引
    function addDataToDynamic(uint256 value) public{
        dynamicData.push(value);
    }
    function removeAllData(uint8 n) public{
        uint256 length = dynamicData.length; 
        for (uint8 i = 0 ;i<length;i++){
            dynamicData.pop(); 
        }
    }
    // 内存当中的动态数组
    // function memoryDynamic(uint8 size) public{
    //     uint256 [] memory temp = new uint256[](size);
    // }
}
// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.2 <0.9.0;


struct Student {
        uint256 num;
        string name;
    }
contract Storage {

    uint256 number;
    
    Student [] public student;

    function setStudent(uint256 _num,string memory _name) public{

        Student memory st = Student(_num,_name);
        // Student memory st = Student({num:_num,name:_name});

        student.push(st);

    }

}

在这里插入图片描述
在这里插入图片描述

引用数据类型

在这里插入图片描述

在solidity当中,引用数据类型的变量和其他语言并不相同,因为solidity当中含有storage这个 数据块的存储位置,所以变量的数据块会固化在存储当中,并不会指向引用的数据变量当中的内容,所以不会出现其他语言当中的引用拷贝,只会出现值拷贝。

	uint256[3] public stateData1;
    uint256[3] public stateData2;
    function assignStateVariable()public{
        stateData1 = stateData2; // 只会把stateData2的值给到stateData1,并不会产生一个指针
        stateData2[0] = 5;
    }
    function storage2memory () public  returns (uint256[3] memory){
        uint256[3] memory data1 = stateData1; // 只会把磁盘当中的数据放到内存当中,不会创建一个指针
        stateData1[0] = 10;
        return data1;
    }
  • location对数据空间的分割
uint256[3] public stateData1;
uint256[3] public stateData2;
function storage2memory () public  returns (uint256[3] memory){
        uint256[3] memory data1 = stateData1; //  两个变量的location是memory和storage,所以是值引用,并且只会把磁盘当中的数据放到内存当中,不会创建一个指针
        stateData1[0] = 10;
        return data1;
}
  • 判定算法
    在这里插入图片描述
	function storage2memory () public {
        uint256[3] memory data;
        data = stateData1; // 因为两个数据的location并不相同,所以不会出现引用拷贝,只会是值拷贝
        stateData1 [0] = 9;
       
    }
    *-------------------------------------------------------
    function storage2memory () public {
        uint256[3] memory data;
        stateData1 = data; 
        stateData1 [0] = 9;
       
    }
    -------------------------------------------------------------------
    uint256[3] public stateData1;       
    uint256[3] public stateData2;
    function assignStateVariable()public{
        stateData1 = stateData2; // 只会把stateData2的值给到stateData1,并不会产生一个指针
        stateData2[0] = 5;
    }
    function storage2memory () public {
        uint256[3] memory data;
        uint256[3] storage sdp = stateData1; // 成员变量默认的location是storage 所以是引用拷贝
        
        sdp = stateData2;
        data = sdp; // 两个变量的location不同 ,所以应该是值拷贝


        stateData1 = data; 
        stateData1 [0] = 9;
       
    }

https://solidity-by-example.org/

函数的调用机制

静态调用

在这里插入图片描述

	// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.2 <0.9.0;


contract Caller{

    uint256 public mycount;

    address callee;

    constructor(address _callee){
        callee = _callee;
    }
    function fetchCallee()public{
        Callee calleeContract = Callee(callee);
        uint256 callee_value = calleeContract.get_number();
        mycount = callee_value;
    }
}

contract Callee{
    uint256 number = 0;
    function get_number()public view returns(uint256) {
        return number;
    }

    function increment() public{
        number += 1;
    }
}

当调用函数和被调用函数不在一个合约当中时,我们需要在调用函数所在的合约当中引入被调用函数所在的合约

import “./callee.sol”;

通过接口调用

在这里插入图片描述
调用者不再需要被调用者的原文件,不需要import导入
需要写一个接口:

// 接口
interface CalleeI{
    
    
    // 接口指向的函数
    function get_number() external view returns(uint256);
}

之后在caller当中修改调用callee函数的语句:

	// 使用接口CalleeI强制转换合约地址为被调用的合约
	CalleeI calleeContract = CalleeI(callee);

之前使用import导入的方式当中调用callee函数的语句:

	Callee calleeContract = Callee(callee);
  • 上课代码

在这里插入图片描述

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.2 <0.9.0;

contract Callee1{
    uint256 count = 0;
    function getCount()public view returns(uint256){
        return count;
    }
    function increment()public{
        count +=1;
    }

}
contract Callee2{
    uint256 count = 0;
    function getCount()public view returns(uint256){
        return count;
    }
    function increment()public{
        count +=2;
    }

}

调用过程中的上下文变量

在这里插入图片描述

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

contract Caller{

    address callee;
    // 调用者拿到被调用者的合约地址
    constructor(address _callee){
        callee = _callee;
    }
    function setCalleeX(uint256 _x) public{
        
        // 把地址强制转换成合约对象
        Callee calleeC = Callee(callee);
        calleeC.setX(_x);
        
    }

}

contract Callee{
    uint256 public x;

    function setX(uint256 _x) public{
        x = _x;
    }
}

关于各个函数合约的调用者

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

contract Caller{

    address callee;

    address public whocallCaller;
    // 调用者拿到被调用者的合约地址
    constructor(address _callee){
        callee = _callee;
    }
    function setCalleeX(uint256 _x) public{
        
        whocallCaller = msg.sender;
        // 把地址强制转换成合约对象
        Callee calleeC = Callee(callee);
        calleeC.setX(_x);
        
    }

}

contract Callee{
    uint256 public x;
    address public whocallCallee;
    function setX(uint256 _x) public{
        whocallCallee = msg.sender;
        x = _x;
    }
}

Caller的调用者是外部账号(eoa),Callee的调用者是Caller

利用上下文变量tx来查看触发transaction的外部账号

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

contract Caller{

    address callee;
    // 在调用链条上出发transaction的外部账号(也就是eoa)
    address public eoa;

    address public whocallCaller;
    // 调用者拿到被调用者的合约地址
    constructor(address _callee){
        callee = _callee;
    }
    function setCalleeX(uint256 _x) public{
        eoa = tx.origin;
        whocallCaller = msg.sender;
        // 把地址强制转换成合约对象
        Callee calleeC = Callee(callee);
        calleeC.setX(_x);
        
    }

}

contract Callee{
    uint256 public x;

    address public whocallCallee;

    address public eoa;
    function setX(uint256 _x) public{
        eoa = tx.origin;
        whocallCallee = msg.sender;
        x = _x;
    }
}

this 关键字

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

contract Caller{

    address callee;
    // 在调用链条上出发transaction的外部账号(也就是eoa)
    address public eoa;

    address public whocallCaller;
    // 调用者拿到被调用者的合约地址
    constructor(address _callee){
        callee = _callee;
    }

    function callSetCalleesetX(uint256 _x) public{
        // 合约内部函数之间的调用    不会产生新的message
        // this关键字会使函数的调用产生新的message  这个message就是这个合约自己不再是eoa
        this.setCalleeX(_x);
    }
    function setCalleeX(uint256 _x) public{
        eoa = tx.origin;
        whocallCaller = msg.sender;
        // 把地址强制转换成合约对象
        Callee calleeC = Callee(callee);
        calleeC.setX(_x);
        
    }

}

contract Callee{
    uint256 public x;

    address public whocallCallee;

    address public eoa;
    function setX(uint256 _x) public{
        eoa = tx.origin;
        whocallCallee = msg.sender;
        x = _x;
    }
}

当合约当中的一个函数要调用另一个可见性为external的函数的时候,必须要使用this关键字 否则会报错

	contract Caller{

    address callee;
    // 在调用链条上出发transaction的外部账号(也就是eoa)
    address public eoa;

    address public whocallCaller;
    // 调用者拿到被调用者的合约地址
    constructor(address _callee){
        callee = _callee;
    }

    function callSetCalleesetX(uint256 _x) public{
        // 合约内部函数之间的调用    不会产生新的message
        // this关键字会使函数的调用产生新的message  这个message就是这个合约自己不再是eoa
        this.setCalleeX(_x);
    }
    function setCalleeX(uint256 _x) external{
        eoa = tx.origin;
        whocallCaller = msg.sender;
        // 把地址强制转换成合约对象
        Callee calleeC = Callee(callee);
        calleeC.setX(_x);
        
    }

}

erc 和 rfc

函数的动态调用

Call调用语法:
在这里插入图片描述

因为call是address的语法,所以调用call的对象必须是一个地址,call的函数是一个字节数组,

在这里插入图片描述
sig是函数的签名 keccak256是一种哈希算法
在这里插入图片描述
把函数签名和参数列表作为参数输入 返回打包之后的列表
动态调用代码:
Callee合约:

	contract Callee{
    uint public x;
    uint public y;
    function setXAndY(uint _x,uint _y) external {
        x = _x;
        y = _y;
    }
}

Caller合约

	contract Caller{
    address callee;

    constructor(address _a){
        callee = _a;
    }

    function setCalleeX(uint _x,uint _y) external{
        // (函数签名,参数列表)
        // 被调合约的函数的签名(只需要参数的类型 不要参数的名称)  参数列表
        bytes memory data = abi.encodeWithSignature("setXAndY(uint256, uint256)",_x,_y); 
        (bool success,bytes memory rdata) = callee.call(data);
        if(!success){
            revert("setXAndY fail!"); // 终止执行
        }

    }
}

调用者合约当中通过函数参数传递然后进行转化的数据就是data 而这个data可以在被调用者合约当中通过上下文变量得到 验证这两个data是相同的

	contract Caller{
    address callee;

    bytes public callee_setxy_data; // 使用编码形成的data

    constructor(address _a){
        callee = _a;
    }

    function setCalleeX(uint _x,uint _y) external{
        // (函数签名,参数列表)
        // 被调合约的函数的签名(只需要参数的类型 不要参数的名称)  参数列表
        bytes memory data = abi.encodeWithSignature("setXAndY(uint256,uint256)",_x,_y); 
        callee_setxy_data = data;
        (bool success,bytes memory rdata) = callee.call(data);
        if(!success){
            revert("setXAndY fail!"); // 终止执行
        }

    }
}



contract Callee{
    uint public x;
    uint public y;
    bytes public callee_data;
    function setXAndY(uint _x,uint _y) external {
        callee_data = msg.data; // 调用者通过函数参数传过来的数据
        x = _x;
        y = _y;
    }
}

备胎fallback函数

在这里插入图片描述

  • fallback函数的可见度必须是external
  • fallback函数的名称就是fallback
  • 书写方式:
	fallback()external{
        // 函数语句
    }

如果在调用者合约当中的的调用函数语句出现了错误,导致无法正常调用应该要调用的函数,就会触发被调用合约当中的fallback函数。

Gas与转账收款

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

Payable关键字(也是函数的一种属性),用于修饰函数,表示该函数可以接受转账,调用该函数当中的data前四位函数选择器对应这个函数时,message当中的value可以大于0,函数当中带的钱会放在value当中。

message : from to value data(前四位表示函数选择器,后边表示函数的参数)

contract Storage {

    uint256 number;

    function store(uint256 num) public payable {
        number = num;
    }
}

在这里插入图片描述
转账收钱

	contract Caller {

    address callee;

    constructor(address _callee){
        callee = _callee;
    }
	// 首先定义一个存钱的函数,才能完成下面转账的功能
    function deposit () public payable {

    }
	// 转账操作
    function callCalllee() public payable {
        
        bytes memory data = abi.encodeWithSignature("receiveMoney()");
        (bool success,bytes memory r_data) = callee.call{value:1 ether}(data);
        if (!success){
            revert("sdajshd  Failed!");
        }
    }
}

contract Callee{

    uint256 number;
    // 使fallback函数也具有收钱的功能
    fallback() external payable{
        
    }

    function receiveMoney () public payable{
        
    }
    
}

如果在交易的时候函数签名出现问题,那么就不能成功完成转账这个操作,这时就要用到fallback函数,为了转账成功,所以要给fallback函数一个payable属性,使他可以收钱。

因为有了fallback函数,所以reteiveMoney函数就不会再起到作用。所以在Caller合约当中调用函数时什么也不调用就可以,传一个空字符串就行。
在这里插入图片描述

10.31

在这里插入图片描述
合约的边界性
非合约地址不一定是合法的外部账号,所以并不一定可以完成接受转账的功能
在这里插入图片描述

11.5

内存动态数组和成员变量动态数组的区别

  • 内存动态数组位于memory 中,成员变量动态数组位于storage中。
  • 成员变量数组初始长度为0,通过push,pop的动态变化,内存动态数组在程序运行时分配固定长度,一旦分配不可变

整型溢出的解决办法

  • 使用高版本
  • 使用safemath库

mapping为什么不能作为public函数的参数

  • 因为public函数可能被链下的其他函数和合约调用,而mapping又不能被拷贝,所以不能作为public函数的参数

引用拷贝和值拷贝

  • 变量本身的指针旋转发生变化,指向其他变量
  • 直接把变量的值拿过来,是数据块本身发生的拷贝

使用mapping和msg
因为java当中引用类型的赋值一定是引用拷贝,不会发生值拷贝

netmask是一个钱包 适用于以太坊和跟他兼容的区块链
web3js 访问合约需要合约地址和abi信息
dapp(decentralized application)去中心化

  • 区块链,智能合约是去中心化的
  • 如果一个应用的核心业务逻辑是构建在区块链和智能合约上就是DAPP

读取合约函数 getOwner 更改合约状态changeOwner

通过import接口调用合约的好处

  • 不容易写错

函数的签名和参数编码 data
函数调用者传过来多少钱 value
合约的调用者 sender
触发调用链条的链下的钱包地址 tx.origin

msg变化规则(沿着调用链条):

  • 函数内部调用msg不变
  • 跨月合约调用,调用者向被调用者发送message,msg发生变化
  • 通过this关键字调用内部函数,msg变化

返回值是struct时,如何解码?

猜你喜欢

转载自blog.csdn.net/ke_en/article/details/142178568