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时,如何解码?

11.07

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

delegatecall详解

delegatecall 语法与动态调用函数相似:
<address>.delegatecall(bytes calldata)
调用别的合约代码,访问自己成员

(bool success,bytes memory rdata) = b.delegatecall(data);

只有方法的名称发生了变化,方法的参数也跟call一样。

在这里插入图片描述
使用Delegatecall方法时B合约当中的msg.sender应该是A的外部账号,也就是Remix账号(不是B合约地址,也不是A的地址),因为Delegatecall修改的是A合约当中的成员变量

	// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.2 <0.9.0;

contract A{
    uint256 public number;
    address b;
    constructor(address _b){
        b=_b;
    }
    function borrowCodeFromB()public{
        bytes memory data = abi.encodeWithSignature("setNumber(uint256)", 13);
        (bool success,bytes memory rdata) = b.delegatecall(data);
        if(!success){
            revert("Fail!");
        }
    }
    function provokeCodeFromB()public{
        bytes memory data = abi.encodeWithSignature("setNumber(uint256)", 14);
        (bool success,bytes memory rdata) = b.call(data);
        if(!success){
            revert("Fail!");
        }
    }
}

contract B{
    uint256 public number;
    function setNumber(uint256 _n)public{
        number = _n;
    }
}
// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.2 <0.9.0;

contract A{
    uint256 public number;
    address public invoker;
    address b;
    constructor(address _b){
        b=_b;
    }
    function borrowCodeFromB()public{
        bytes memory data = abi.encodeWithSignature("setNumber(uint256)", 13);
        (bool success,bytes memory rdata) = b.delegatecall(data);
        if(!success){
            revert("Fail!");
        }
    }
    function invokeCodeFromB()public{
        bytes memory data = abi.encodeWithSignature("setNumber(uint256)", 14);
        (bool success,bytes memory rdata) = b.call(data);
        if(!success){
            revert("Fail!");
        }
    }
}

contract B{
    uint256 public number;
    address public invoker;
    function setNumber(uint256 _n)public{
        invoker = msg.sender;
        number = _n;
    }
}

部署合约之后发现A合约当中的invoker发生了变化但是B合约当中的invoker并没有发生变化,就是因为Delegatecall方法时调用合约B的代码在合约A当中去执行,所以修改的是A合约当中的成员变量。
但是调用invokeCodeFromB函数(因为这是一个正常的函数调用)就会发现A当中的invoker并没有发生变化,而是B合约当中的invoker发生了变化,并且invoker当中的内容是A合约的地址。
因为Delegatecall方法要求两个合约当中的plot一样的,所以如果两个合约当中的成员变量的顺序没有对应上,那么就会产生错误。invoker就无法发生正常的变化。

	// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.2 <0.9.0;

contract A{
    uint256 public number;
    address public invoker;
    address b;
    constructor(address _b){
        b=_b;
    }
    function borrowCodeFromB()public{
        bytes memory data = abi.encodeWithSignature("setNumber(uint256)", 13);
        (bool success,bytes memory rdata) = b.delegatecall(data);
        if(!success){
            revert("Fail!");
        }
    }
    function invokeCodeFromB()public{
        bytes memory data = abi.encodeWithSignature("setNumber(uint256)", 14);
        (bool success,bytes memory rdata) = b.call(data);
        if(!success){
            revert("Fail!");
        }
    }
}

contract B{
    uint256 public number;
    address public invoker;
    function setNumber(uint256 _n)public{
        invoker = msg.sender;
        number = _n;
    }
}

代理模式

代理模式为了完成合约的升级

在这里插入图片描述

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.2 <0.9.0;


contract Proxy {
    uint256 public x;
    address logic;

    constructor(address l){
        logic = l;
    }
    fallback() external {
        (bool success,bytes memory rdata) = logic.delegatecall(msg.data);
        if (!success){
            revert("fail!");
        }
    }
    
}

contract Logic{

    uint256 public x;
    function increment( ) public {
        x++;
    }

}

contract Client{
    address public proxy;
    constructor(address p){
        proxy = p;
    }
    function callProxyNoneExist()public{
        bytes memory data = abi.encodeWithSignature("increment()");
        (bool success,bytes memory rdata)=proxy.call(data);
        if (!success){
            revert("fail!");
        }
    }
}
	// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.2 <0.9.0;


contract Proxy {
    uint256 public x;
    address logic;

    constructor(address l){
        logic = l;
    }
    fallback() external {
        (bool success,bytes memory rdata) = logic.delegatecall(msg.data);
        if (!success){
            revert("fail!");
        }
    }
    function upgradeTo(address newlogic) public {
        logic = newlogic;
    }
    
}


contract Logic{

    uint256 public x;
    function increment( ) public {
        x++;
    }

}
contract NewLogic{

    uint256 public x;
    function increment( ) public {
        x += 5;
    }

}

contract Client{
    address public proxy;
    constructor(address p){
        proxy = p;
    }
    function callProxyNoneExist()public{
        bytes memory data = abi.encodeWithSignature("increment()");
        (bool success,bytes memory rdata)=proxy.call(data);
        if (!success){
            revert("fail!");
        }
    }
}

在这里插入图片描述

11.14

因为fallback没有返回值,所以调用者看不到调用的结果。

可以使用汇编语言来完成这个操作,可以使fallback把结果返回

部署一个proxy(代理)合约 再部署一个logic(逻辑)合约 再使用proxy的地址来把代理合约强制当成一个logic合约使用,此时操作的是proxy当中的数据,可以查看logic合约当中的数据被没有发生变化。

但是为了使proxy合约和logic合约当中的slot相对应,需要在logic合约当中设置一个placeholder相当于占了一个位置。为了更简洁,再次使用汇编语言完成这个操作

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.2 <0.9.0;

// 代理合约
contract Proxy {

    // constant 常量  不占用存储槽 
    bytes32 private constant logicPosition = keccak256("org.zeppelinos.proxy.implementation"); 
    uint public x = 10;
    address public logic;
    function upgradeTo(address newLogic)public {   
        bytes32 position = logicPosition;
        assembly{
            sstore(position,newLogic)
        }
    } 
    function mylogic() public view returns(address impl) {   
        bytes32 position = logicPosition;   
        assembly {
            impl := sload(position)
        } 
    } 
   

    constructor(address l){
        logic = l;
    }

    fallback() external {
        address _logic = logic;
         assembly {
            // Copy msg.data. We take full control of memory in this inline assembly
            // block because it will not return to Solidity code. We overwrite the
            // Solidity scratch pad at memory position 0.

            // calldatacopy(t, f, s) - copy s bytes from calldata at position f to mem at position t
            // calldatasize() - size of call data in bytes
            calldatacopy(0, 0, calldatasize())

            // Call the implementation.
            // out and outsize are 0 because we don't know the size yet.

            // delegatecall(g, a, in, insize, out, outsize) -
            // - call contract at address a
            // - with input mem[in…(in+insize))
            // - providing g gas
            // - and output area mem[out…(out+outsize))
            // - returning 0 on error (eg. out of gas) and 1 on success
            let result := delegatecall(gas(), _logic, 0, calldatasize(), 0, 0)

            // Copy the returned data.
            // returndatacopy(t, f, s) - copy s bytes from returndata at position f to mem at position t
            // returndatasize() - size of the last returndata
            returndatacopy(0, 0, returndatasize())

            switch result
            // delegatecall returns 0 on error.
            case 0 {
                // revert(p, s) - end execution, revert state changes, return data mem[p…(p+s))
                revert(0, returndatasize())
            }
            default {
                // return(p, s) - end execution, return data mem[p…(p+s))
                return(0, returndatasize())
            }
        }
    }

}

// 逻辑合约
contract Logic{
    // address public placeholder; // 为了使slot与proxy当中的slot相对应
    // 去掉placeholder之后,使用汇编语言来完成两个合约当中的slot相对应
    uint public x;
    uint public y;
    function getX() public view returns(uint256){
        return x;
    }
    function getY() public view returns(uint256){
        return y;
    }
    function setX(uint256 _x) public {
        x = _x;
    }
    function setY(uint256 _y) public {
        y = _y;
    }

}
// 把代理合约强制当成logic合约来用,此时操作的数据是proxy当中的数据,可以查看logic合约当中的数据并没有发生变化

11.19

using的用法

	// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.2 <0.9.0;

library Math {
    function sqrt(uint y) internal pure returns (uint z) {
        if (y > 3) {
            z = y;
            uint x = y / 2 + 1;
            while (x < z) {
                z = x;
                x = (y / x + x) / 2;
            }
        } else if (y != 0) {
            z = 1;
        }
        // else z = 0 (default value)
    }
    function add(uint256 x,uint256 y) internal pure returns(uint256){
        return x+y;
    }
}
contract Storage {
    // 第一种方法 进行绑定
    using  Math for uint256; // 与整形数绑定
    function computeSqrt(uint256 x) public pure returns(uint256){
        uint256 r = x.sqrt();
        return r;
    }
    // 第二种方法  不绑定
    /*
    function computeSqrt(uint256 x) public pure returns(uint256){
        uint256 r = Math.sqrt(x);
        return r;
    }
    */
    

}

在部署合约的时候会部署两次,因为library库当中有一个可见性为public的函数,会把它当成一个合约来部署。

	// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.2 <0.9.0;

library FooContract {
    
    function foo(address addr) internal  {
        bytes memory cd = abi.encodeWithSignature("foo()");
        (bool success,bytes memory rd) = addr.call(cd);
        if(!success){
            revert("Fail!");
        }
    }

}
contract Storage {
    address fooaddr;
    using FooContract for address;

    function callFoo() public {
        fooaddr.foo();
    }
}

多继承

在这里插入图片描述

	// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.2 <0.9.0;


contract ParentContract {
    
    uint256  number;
    event Store(uint256 _old ,uint256 _new);
    modifier lessthan(uint256 num ){
        require(num <= 100,"failed!");
        _;
    }

    constructor(uint256 _n){
        number = _n;
    }
    



    function store(uint256 num) internal lessthan(num) {
        emit Store(number,num);
        number = num;
    }


    function retrieve() public view returns (uint256){
        return number;
    }
}

// 继承了ParentContract合约
contract ChildContract is ParentContract{
    // 子合约的构造函数调用父合约的构造函数    并且传入一个参数
    constructor( uint256 _n ) ParentContract ( _n){

    }

/*
    // 直接调用父合约的构造函数并且传入 45
    constructor() ParentContract (45){

    }
*/

    function foo(uint256 num) public lessthan(num) {
        emit Store(number,num);
        number = num;
        retrieve();
    }
}
	// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.2 <0.9.0;



abstract contract  A{
    uint256 number;

    // 当一个virtual 函数有函数体之后就可以去掉abstract

    function foo(uint256 _n) external virtual;
}

// 一个抽象合约被继承  其中包括一个虚函数  那么子合约就可以实现这个虚函数
contract Child is A{
    // 使用override关键字 
    function foo(uint256 _n) external override{
        number = _n;
    }
}

合约的安全问题

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值