solidity快速入门


文章 , 技术分享2215 阅2 评

solidity快速入门

从实战角度讲解solidity
--- 学无止尽,本篇文章会持续更新

在变量前使用public修饰等于自动帮他创建一个get函数

不可变类型

在 Solidity 中,constant 是一个修饰符,用于声明函数的状态。它指示函数不会修改或访问合约的状态。具体来说,constant 表示一个函数是只读的,不会修改合约中的数据。在变量前加一个constant表示变量不可被修改

string constant str001 = "hell";
//constant修饰的不可变量
//SPDX-License-Identifier:MIT
pragma solidity ^0.8.18;

contract Constants {
    string public constant addr1 = "0x1"; // 定义常量不可更改
    // string public immutable addr2 = "0x2";  //string无法使用immutable
    address public constant addr3 = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4; 

    function setAddr1() public {
        // addr1 = "0x111"; // 这行代码会报错,因为 addr1 是 constant 的
    }
}
//immutable修饰的不可变量
//SPDX-License-Identifier:MIT
pragma solidity ^0.8.18;
contract Immutable{
    bytes32 public immutable STR;
    address public address1 ;
    // 合约部署时自动初始化的值 ,immutable只能在constructor初始化时才能修改/定义
    constructor(){
        STR = "hi";
        address1 = msg.sender;
    } 
}

函数处理变量

在 Solidity 中,有几种不同的数据位置用于处理函数参数和变量。这些位置包括 memorystoragecalldata

  • memory:表示数据将被存储在临时内存中。这适用于函数内部创建的变量或函数参数,因为它们只在函数执行期间存在,并在函数执行后被清除。
  • storage:表示数据将被永久存储在区块链的状态中。这适用于合约的状态变量,它们在合约被部署后一直存在。
  • calldata:表示数据位于交易的输入数据中,它在函数调用期间被读取。这适用于函数的参数,特别是在从外部调用合约时,参数通常会出现在事务数据中。

在 Solidity 函数中,参数的数据位置默认情况下是 calldata。但是对于字符串类型,必须显式指定数据位置为 memorycalldata,以便在函数内部使用它们。

//SPDX-License-Identifier:MIT
pragma solidity ^0.8.18;
contract getandset{
    string public name1 ;
    function  set(string memory _name) public{
        name1 = _name;
    }
    function  get()view public returns(string memory greeting){ 
        return name1;
    }
}    

在remix中查看函数返回值

function  get()view public returns(string memory a){
        a = "11";  //直接复制returns的参数,不需要写return可以直接进行赋值返回
 }

在有多个变量的时候可以更好的展示,用前缀greeting(自定义的),在获取实际数据的时候不会展示前缀,也可以理解为更清晰的让你看懂这个代码返回的参数是什么意思

function返回值查看

function函数返回值

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

contract Function {
    // Functions can return multiple values.
    function returnMany() public pure returns (uint, bool, uint) {
        return (1, true, 2);
    }

    // Return values can be named.
    function named() public pure returns (uint x, bool b, uint y) {
        return (1, true, 2);
    }

    // Return values can be assigned to their name.
    // In this case the return statement can be omitted.
    function assigned() public pure returns (uint x, bool b, uint y) {
        x = 1;
        b = true;
        y = 2;
    }

    // Use destructuring assignment when calling another
    // function that returns multiple values.
    function destructuringAssignments()
        public
        pure
        returns (uint, bool, uint, uint, uint)
    {
        (uint i, bool b, uint j) = returnMany(); //调用上面的函数进行返回值传参

        // Values can be left out.
        (uint x, , uint y) = (4, 5, 6);  //相当于x=4  y=6

        return (i, b, j, x, y);
    }

    // Cannot use map for either input or output

    // Can use array for input
    function arrayInput(uint[] memory _arr) public {}

    // Can use array for output
    uint[] public arr;

    function arrayOutput() public view returns (uint[] memory) {
        return arr;
    }
}
// Call function with key-value inputs
contract XYZ {
    function someFuncWithManyInputs(
        uint x,
        uint y,
        uint z,
        address a,
        bool b,
        string memory c
    ) public pure returns (uint) {}

    function callFunc() external pure returns (uint) {
        return someFuncWithManyInputs(1, 2, 3, address(0), true, "c");
    }

    function callFuncWithKeyValue() external pure returns (uint) {
        return
            someFuncWithManyInputs({a: address(0), b: true, c: "c", x: 1, y: 2, z: 3});
    }
}

modifier自定义修饰器

修饰符可以传参

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
contract modifiertest{
    address public owner;
    string public name;
    constructor(){
        owner = msg.sender;
        name = "nbsb";
    }
    modifier OnlyOwner(){
        require(msg.sender == owner,"you are not contract owner");
        _; //要在require后面 继续下面的函数主体的意思
    }
    modifier NotGtZero(string memory _name){  //modifier可以接受参数
        require(bytes(_name).length >0 , "invalid Name");
        _;
    }
    modifier VaildAddress(address _address){  
        require(_address != address(0) , "invalid address"); //可以判断账户地址是否有效,需要存在才可以
        _;
    }
    function setName(string memory _name) public OnlyOwner NotGtZero(_name){ //使用修饰器这样就会先判断调用者是否符合条件(是否是拥有者)/ 还可以判断name是否为0 使用了2个modifier
        name = _name;
    }
    // // 等同于这样
    // function setName(string memory _name) public OnlyOwner NotGtZero(_name){ 
    //     require(msg.sender == owner,"you are not contract owner");
    //     require(bytes(_name).length >0 , "invalid Name");
    //     name = _name;
    // }
    function getName() public view returns(string memory) {
        return name;
    }
}

函数修饰符可以在函数主题之前条用 也可以在函数主题之后甚至中间被调用

//例如这样可以防止重注攻击
modifier noReentrancy() {
    require(!locked, "No reentrancy");

    locked = true;
    _;
    locked = false;
}
pragma solidity ^0.8.17;
contract ExampleContract {
    address public owner;
    uint public balance;
    bool public locked;
    constructor() {
        owner = msg.sender;
    }
    modifier onlyOwner() {
        require(msg.sender == owner, "Not owner");
        _;
    }
    modifier noReentrancy() {
        require(!locked, "No reentrancy");
        locked = true;
        _;
        locked = false;
    }
    function withdraw(uint _amount) public onlyOwner noReentrancy {
        require(balance >= _amount, "Insufficient balance");
        // 假设在取款时,需要执行一些复杂的逻辑或调用其他合约。
        // 在这个例子中,我们调用了一个名为`reentrantFunction`的函数,这个函数可能会尝试重入`withdraw`函数。
        // 重入比如就是 在调用一次 withdraw 函数
        // 正常的取款逻辑
        balance -= _amount;
        // 调用可能尝试重入的函数
        reentrantFunction(_amount);
    }
    function reentrantFunction(uint _amount) private {
        require(balance >= _amount, "Insufficient balance");
        // 在这里进行重入攻击的尝试
        // 假设攻击者尝试进行转账操作,从该合约再次调用`withdraw`函数
        // 没有使用修饰器的情况下,重入攻击可能会导致合约的余额被重复转出。
        // 使用了`noReentrancy`修饰器后,重入攻击会被检测并阻止。
        // 注意:此处为了演示目的,没有进行实际的转账操作。
        // 实际应用中,应该根据业务逻辑来处理资金转移。
        balance -= _amount;
    }
}

修饰器noReentrancy用来防止重入攻击。它通过一个名为lock的布尔变量来管理函数的执行权限。当一个函数被调用时,首先要检查lock变量的值,如果为false,则将lock设置为true,表示函数正在执行。然后执行函数的代码(通过_占位符),最后将lock设置为false,表示函数执行完成。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
contract modifiertest{
    bool public locked;
    int public x;
    modifier noReentrancy() {
        require(!locked, "No reentrancy");
        locked = true;
        _;
        locked = false;
        }
    function decrement(int i) public  noReentrancy { //因为加入了noReentranct
        x -= i;
        if (i > 1) {
            decrement(i - 1);  //这里重新调用此函数的时候会抛出错误
        }
    }
}

调用合约功能后返回的参数

Events 进行记录

可以进行监听事件

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
contract Event{
//使用 indexed 关键字可以为事件参数创建索引,从而提高事件过滤和搜索的效率
//只能通过列入js等进行过滤索引
    event Log(address indexed sender , string message);
    event AnotherLog(); 
    function test() public{
        emit Log(msg.sender,"hhh");
        emit Log(msg.sender,"nihao");
        emit Log(msg.sender,"hello world");
        emit Log(msg.sender,"nbsb is me");
        emit AnotherLog(); //只触发了这个事件,但是并没有传递任何参数
    }
}

pure和view的相同点和不同点

pureview 都是 Solidity 中的函数修饰符,用于声明一个函数不会修改状态。它们之间的共同点和不同点如下:

共同点:

  • 都用于标识函数不会对区块链状态产生改变。
  • 都仅能读取数据而不能修改数据。

不同点:

  • pure 修饰的函数不能读取或修改除函数参数以外的任何数据。它是用于执行纯粹的计算,不涉及任何外部信息的函数。例如,计算两个数的乘积而不涉及合约状态或外部调用的函数,可以使用 pure 修饰。
  • view 修饰的函数可以读取合约内的数据,但不能修改任何状态。它用于函数在执行期间只读取数据的情况。例如,读取合约的状态变量或调用其他 view 函数的函数,可以使用 view 修饰。

在选择使用哪个修饰符时,根据你的需求来确定:

如果函数内部只执行计算操作而不涉及合约状态和其他外部信息,可选用 pure,这样会使函数更明确且限制了函数的行为范围。

如果函数仅读取数据而不修改状态,而且可能需要访问合约内的状态变量或调用其他 view 函数,可选用 view

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
contract ifElse {
    //不涉及合约状态和其他外部信息,直接使用pure
    function iforno(uint x) public pure returns(string memory) {
        if(x < 10){
            return "lt 10";
        } else if(x < 20){
            return "lt 20";
        } else {
            return "gt 20";
        }
    }
    function ternary(uint _x) public pure returns(string memory){
        return _x >20 ? "gt 20" :"lt 20"; //三元运算符
    }
}
//SPDX-License-Identifier:MIT
pragma solidity ^0.8.18;
contract getandset{
    string public name1 ;
    function  set(string memory _name) public{
        name1 = _name;
    }
    //设计合约状态和需要用合约中的参数可以用view
    function  get()view public returns(string memory greeting,int numberNB){
        return (name1,1);
    }
}

mapping

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

contract mpas {
    mapping(int=>address) public mapss ;
     string public aa ;
    function addmap(int x,string memory y) public {
        mapss[x] = msg.sender;
        aa = y;
    }
    function get(int x2) public view returns(address){
        return mapss[x2];
    }
}

Array

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
contract Array013{
    uint[] public arr1 = [1,2,3,4,5]; //动态数组初始长度
    uint[10] public arr2 ;  //固定长度默认每个元素都是0 / 不支持push
    function getArray1() view public returns(uint[] memory) {
        return arr1;
    }
    function getArray2() view public returns(uint[10] memory){
        return arr2;
    }
    function pushArr1(uint x) public {
        arr1.push(x);
    }
    function pushArr2(uint x , uint y ) public {
        arr2[x] = y;
    }
    function popArray1()  public {
        arr1.pop(); //删除最后一个数字
    }// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
contract Array013{
    uint[] public arr1 = [1,2,3,4,5]; //动态数组初始长度
    uint[10] public arr2 ;  //固定长度默认每个元素都是0 / 不支持push
    function getArray1() view public returns(uint[] memory) {
        return arr1;
    }
    function getArray2() view public returns(uint[10] memory){
        return arr2;
    }
    function pushArr1(uint x) public {
        arr1.push(x);
    }
    function pushArr2(uint x , uint y ) public {
        arr2[x] = y;
    }
    function popArray1()  public {
        arr1.pop(); //删除最后一个数字
    }
    function getArray1length()  public view returns(uint){
        return arr1.length; //返回长度
    }
    function deleteArray1(uint index ) public {
        delete arr1[index]; //并不是删除,是将这个索引的值转为初始值也就是0(使用pop可以删除最后一个元素)
    }
    function examples() external{
        uint[] memory a = new uint[](5); //创建一个只有5个数组的array
    } 
}

删除数组中的指定索引的元素,实际上就是替换数组指定索引依次替换为后一位,然后使用pop将最后一个元素进行删除,即可完成替换

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
contract removeArray{
    // [1, 2, 3] -- remove(1) --> [1, 3, 3] --> [1, 3]
    // [1, 2, 3, 4, 5, 6] -- remove(2) --> [1, 2, 4, 5, 6, 6] --> [1, 2, 4, 5, 6]
    // [1, 2, 3, 4, 5, 6] -- remove(0) --> [2, 3, 4, 5, 6, 6] --> [2, 3, 4, 5, 6]
    // [1] -- remove(0) --> [1] --> []
    uint[] public arr= [1,2,3,4,5,6,7,8];
    function remove(uint _index) public {
        require(_index < arr.length, "index out of bound");
        for (uint i = _index; i < arr.length - 1; i++) {
            arr[i] = arr[i + 1];
        }
        arr.pop();
    }
    //这个方法是直接将最后一个元素和自己需要删除的元素进行替换,替换后删除最后一个元素,缺点就是顺序会变,如果没有顺序需求可以用这个方法,效率会高一些
    function remove001(uint _index) public {
        require(_index < arr.length, "index out of bound");
        arr[_index] = arr[arr.length -1];
        arr.pop();
    }
    function getArray() view public returns(uint[] memory){
        return arr;
    }
}

枚举体Enum

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
contract Enum{
    enum Status{Pening,shiipped,nbsb}
    Status public status;
    function get() public view returns(Status){
        return status; //获取的是这个枚举体的索引
    }
    function getStatus() public view returns (string memory) {
        if (status == Status.Pening) {
            return "Pening";
        } else if (status == Status.shiipped) {
            return "shiipped";
        } else if (status == Status.nbsb) {
            return "nbsb";
        } else {
            revert("Invalid status");
        }
    }
    function set(Status _status) public { //就算设置也只能设置上面枚举的索引,不然会报错
        status = _status;
    } 
    function setPening() public {
        status = Status.Pening;//设置这个枚举具体数的索引(这个函数表示为设置为Pening的索引)
    }
    // delete resets the enum to its first value, 0
    function reset() public {
        delete status; //将这个值还原
    }
}

这个也是可以直接将枚举体放到一个文件中,在另一个文件种引用这个枚举体enum的文件夹

image-20230720151617894

结构体struct

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
contract Struct{
    struct Person{
        string name;
        int age;
        int id;
    }
    Person[] public persons; 
    function add(string memory _name, int _age, int _id) public{
        require(bytes(_name).length > 0  ,"invild _name");
        require(_age != 0 ,"invild age");
        require(_id != 0 ,"invild id");
        Person memory people =  Person(_name,_age,_id);
        // Person memory people =  Person({name:_name,id:_id,age:_age}); //使用参数传入,不需要位置一一对应就可以进行传参
        //还有一个方法就是新建一个Person的对象然后将对象的每个值分别复制后在push到这里,推荐直接使用第一种方法简洁明了
        persons.push(people);
    }
    function getPersonName(uint _index) view public returns(string memory){
        require(_index < persons.length ,"please input lt persons index");
        return persons[_index].name ;
    }
    function getPersonN(uint _index) view public returns(Person memory){
        require(_index < persons.length ,"please input lt persons index");
        return persons[_index] ;
    }
    //修改这个值
    function updata(uint x , string memory _name)  public {
        persons[x].name = _name;
    }
}

这个也是可以直接将结构体放到一个文件中,在另一个文件种引用这个结构体struct的文件夹


修饰符

  1. pure:该修饰符用于声明函数不会读取或修改合约的状态,并且不会调用其他的非pure或非view函数。这意味着pure函数不会有副作用,只返回根据输入参数计算得出的结果。使用pure修饰符可以让编译器优化函数的执行。
  2. const:该修饰符是pure的同义词,在Solidity 0.6.0之前的版本中使用。在Solidity 0.6.0及更高版本中,建议使用pure而不是const
  3. view:该修饰符用于声明函数只读取合约的状态,而不会修改合约的状态。view函数可以调用其他的viewpure函数,但不可以调用其他类型的函数,包括non-payablepayable函数。与pure函数类似,view函数也可以让编译器进行优化。

在Solidity中,可以使用pure修饰符来声明函数时不会访问或修改合约状态的纯函数。纯函数只依赖于输入参数,并根据输入参数计算并返回结果,对合约状态没有任何影响。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

contract MathOperations {
    function add(uint a, uint b) public pure returns (uint) {
        return a + b;
    }

    function multiply(uint a, uint b) public pure returns (uint) {
        return a * b;
    }

    function exponentiation(uint base, uint exponent) public pure returns (uint) {
        return base ** exponent;
    }
}

将函数映射到一个foods中,用的时候可以直接实例化出来

mapping (uint256 => address)  foods;

FoodInfoItem food = new FoodInfoItem(name, traceName, quality, msg.sender); 

foods[traceNumber] = food;

FoodInfoItem(foods[traceNumber])  //可以直接映射出来使用
//SPDX-License-Identifier:MIT
pragma solidity ^0.8.18;
contract Variablez{
    //使用type函数获取变量最大值最小值
    int32 public a = type(int32).max;
    function dosomthing() public view returns(uint) {
        uint number = 99;
        uint time = block.timestamp;
        address sender = msg.sender;
        return time;
    }
}

合约继承

想要在子合约中继承可以使用 is 进行继承

重写父合约方法可以在子合约函数上用override修饰符同时父合约要加 virtual修饰符

//子合约中
function dosomthing() public pure override returns(uint)  {
        uint number = 88;
        return number;
 }
 //父合约中
 function dosomthing() public pure virtual returns(uint) {
        uint number = 99;
        return number;
 }

余额转移

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

contract DappTest{
    uint public aa ;
    uint public bb = 100;
    function found() payable public{
        require(msg.value > 100 wei,"not great than 100wei");
    } 
    function withdraw() public{
        aa = address(this).balance ;
        bb = 100;
        
        //取钱三种方法: send transfer call  //send和transfer显示2300gas call不限制
        // msg.sender 是接收方地址  address(this)是合约地址

        // payable(msg.sender).transfer(address(this).balance);

        // bool senSuccess =  payable(msg.sender).send(address(this).balance);
        // require(senSuccess,"send failed");
        
        // 这个bytes直接设置为空的,所以可以直接写不接受也可以
        (bool callSuccess ,bytes memory a) = payable(msg.sender).call{value:aa}("");
        require(callSuccess,"failed");
    }
    
}

library

using的前提是需要直接或者间接的导入某个library.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
// 定义一个库合约
library MathUtils {
    function add(uint a, uint b) public pure returns (uint) {
        return a + b;
    }
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import "./useing1.sol";
// 使用库合约
contract MyContract {
    // 引入库合约
    using MathUtils for uint;  //只要是uint类型的都可以调用MathUtils中的函数
    function myFunction(uint a, uint b) public pure returns (uint) {
        // 调用库合约中的函数
        return a.add(b); 
    }
}

这样说更容易理解: using A for B,这里A通常是某个library,B是某种数据类型,这句话是把A中的方法绑定到B类型上,相当于给B类型附加了A库中的方法。(也有翻译为附着库的) 比如 using SafeMath for uint256,SafeMath是库,for uint256,表示,SafeMath库中的方法绑定到uint256这个类型,那么uint256类型的实例可以直接调用SafeMath库中的所有方法; 然后库SafeMath里面有定义一个add(uint256 a,uint256 b)方法。 我们有一个uint256 c;那么可以这样调用c.add(d),执行结果相当于c+d, add方法在定义的时候,第一个参数会是一个uint256类型的变量,表示调用者。

实战用例:(将类型赋值为library中的struct变量,这样就可以只能使用这个赋值的变量进行调用了)

library使用

为什么这里使用的storage而不使用memory?
为什么这里使用的storage而不使用memory?

使用 storage 主要是因为需要修改存储位置上的数据或读取最新的存储位置。当需要直接访问或修改合约的状态变量时,就需要使用 storage。而使用 memory 是为了临时存储和操作数据,在函数执行结束后会被清除。

uint[0]和uint[](0)的区别

uint[0]uint[](0) 在 Solidity 中有不同的含义:

  1. uint[0]:表示一个固定大小为 0 的无符号整数数组。这个数组没有任何元素,因此它没有分配任何存储空间。这种数组在编译时大小就确定了,不能增加或减少元素。
  2. uint[](0):表示一个动态大小的无符号整数数组,并且该数组没有任何元素。这个数组是通过动态分配的,在运行时可以根据需要增加或减少元素。

总结来说,uint[0] 是固定大小的数组,且大小为 0,而 uint[](0) 是动态大小的数组,且初始时没有元素。

在实际使用中,通常使用 uint[] 表示动态大小的数组,而不是 uint[0]。因为 uint[0] 没有任何实际存储空间,而动态数组可以根据需要进行大小调整。

uint[](0)创建有0个参数的动态数组

在直接对struct结构体进行实例化时,uint[](0)需要使用new进行创建,否则会报错Explicit type conversion not allowed from "int_const 0" to "uint256[] memory".

Company memory newCompany = Company(name,companyAddress,uint(0),new uint[](0),new uint[](0) );

在solidity8版本之前声明uint[0]会直接报错

keccak256函数比较空字符串

keccak256() 函数可以传递可变数量的参数,你可以将任意数量的值作为参数传递给该函数。这些值将在进行哈希计算之前被编码和混合在一起。例如,你可以使用以下方式传递多个参数给 keccak256() 函数:keccak256(value1, value2, value3, ...)在函数内部,所有传递的参数将会被编码和混合,然后进行 Keccak-256 哈希计算,最终返回一个 256 位的哈希值。需要注意的是,传递给 keccak256() 函数的参数的类型必须是 Solidity 支持的合法类型,例如整数、字符串、字节数组等。如果传递的参数类型不正确,编译器将会报错。

这段代码使用了 Solidity 中的 keccak256 函数来比较 bank.bankName 是否等于一个空字符串。

在 Solidity 中,keccak256 是一个哈希函数,它会接收一个字节数组作为参数,并返回该字节数组的 keccak256 哈希值。

在这段代码中,keccak256(bank.bankName)keccak256("") 分别计算了 bank.bankName 和空字符串的 keccak256 哈希值。然后使用 == 运算符来比较这两个哈希值是否相等。

如果 bank.bankName 和空字符串的 keccak256 哈希值相等,说明 bank.bankName 是一个空字符串,这表示银行的名称没有被设置。在这种情况下,代码返回 404001。否则,返回值将取决于代码的其他部分,这段代码没有提供更多的信息。

需要注意的是,keccak256 是一种哈希算法,它确保即使输入只有微小的变化,哈希值都会有显著的不同。这使得 keccak256 适用于许多加密和完整性校验的应用场景。

if (keccak256(name) == keccak256("")) 
{ return 404001; }

引入外部合约并实例化进行调用他的方法

调用的地址可以是合约地址

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0 <=0.8.20;
pragma experimental ABIEncoderV2;

import "./test88.sol";

contract test888{
    address public tt;
    address public testadd;
    
    function test() public returns(address){
        test88 adddr = new test88();
        tt = address(adddr);
        testadd = adddr.get();
        return testadd;
    }
    // test88 adddr = (new test88){value:msg.value}(nbsb,1123); //如果有需要msg.value值的话就需要使用这样方法示例创建,constructor中需要传入两个值进行创建的话
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0 <=0.8.20;
pragma experimental ABIEncoderV2;
contract test88{
    address public owner;
    
    constructor(){
        owner = msg.sender;
    }
    function get() view returns(address){
        return owner; //返回的是上面的那个合约 的合约地址
    }
}

可以进行限制这个合约只能在那个合约中使用

 require(_owner == msg.sender, "only trace contract can invoke"); 
 //必须是来自trace合约的地址调用的才可以,在trace函数中,使用new实例化这个内容在进行调用,则的调用地址就是trace合约的地址

接口和抽象合约有什么不同?

interface abstract

pragma solidity ^0.6.0;
//请在此处定义接口:
interface Calculator{
    function getResult() external view returns(uint);
}

contract Test is Calculator{
//请在下方一行写出合约继承接口的语句:
   
    //请在下方一行写出继承的函数定义及其内容:   
    function getResult() external view returns(uint){
        uint a = 1;
        uint b = 2;
        uint result = a+b;
        return result;
    }
pragma solidity ^0.6.0;

//请在下方定义抽象合约:
abstract contract Calculator{
    function getResult() public view virtual returns(uint);  //使用virtual注解意思是可以进行重写
}


//请在下方一行定义继承合约的语句:
contract Test is Calculator{
    //请在下方一行写出定义函数的语句:
    function getResult() public view override returns(uint)
    {
      uint a = 1;
      uint b = 2;
      uint result = a + b;
      return result;
   }
}

solidity中的错误处理

pragma solidity ^0.6.0;

contract Account {
    uint public balance;
    //下方一行没有实际用途,仅作为提示:整形变量的最大数值,超过就会上溢。
    uint public constant MAX_UINT = 2 ** 256 - 1; 

    function deposit(uint _amount) public {
        uint oldBalance = balance;
        uint newBalance = balance + _amount;
// 请在下方一行写出第一个 require 语句:
        require(newBalance>= oldBalance,"上溢了哦");
        balance = newBalance;
// 请在下方一行写出第一个 assert 语句:
        assert(balance>=oldBalance);

    }

    function withdraw(uint _amount) public {
        uint oldBalance = balance;
    // 请在下方一行写出第二个 require 语句:
    require(balance>=_amount,"下溢了哦");
        // 请在下方写出 if 条件语句:
        if(balance< _amount){
            revert("下溢了哦");
        }
        balance -= _amount;
// 请在下方一行写出第二个 assert 语句:
        assert(balance<=oldBalance);
    }
}

委托调用

委托这个函数代码进行调用,但是实际更新的是自己函数的值,也是代码复用的一种

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;


contract TestDelegateCall {
    uint public num;
    address public sender;
    uint public value;
    function setVars(uint _num) external payable{
        num = _num;
        sender = msg.sender;
        value = msg.value;

    }
    
}
contract DelegateCall{
    uint public num;
    address public sender;
    uint public value;
    //调用后更新自己的值,而没有更新上面函数的这几个值
    function setVars(address _test,uint _num) external payable{
    (bool success , bytes memory data ) =  _test.delegatecall(abi.encodeWithSelector(TestDelegateCall.setVars.selector,_num));
        require(success,"fail ");
    }
}

solidityTable的使用

// SPDX-License-Identifier: UNLICENSED
// SPDX-License-Identifier: MIT
pragma solidity ^0.4.25;
pragma experimental ABIEncoderV2;
import "./Table.sol";
contract Test3 {
    TableFactory tf;
    string constant TABLE_NAME = "tb_user33";
    // uint count ;
    constructor()  {
        tf = TableFactory(0x1001);
        tf.createTable(TABLE_NAME, "id", "username,password");
    }
    function insertTest(string memory _id,string memory _username,string memory _password) public returns(int) {
        Table table = tf.openTable(TABLE_NAME);
        //Entry类似于映射实体类对象
        Entry entry = table.newEntry(); // Entry 相当于设置了一个类似于java中的bean
        // count +=1;
        entry.set("id",_id);
        entry.set("username", _username);
        entry.set("password", _password);
        int res = table.insert(_id, entry);
        return res;
    }
    function selectById(string memory _id,string memory _username) public view returns(string memory,string memory){
        Table table = tf.openTable(TABLE_NAME);
        Condition  condition = table.newCondition(); // Condition 就相当于mybatis plus中的wapper,用于设置查询条件
        condition.EQ("username",_username); // 将 _id 参数传递给条件对象
        Entries entries = table.select(_id, condition);  //Entries获取到的是一个数据集,获取数据集中第一个即可得到这个映射的数据
        // 检查是否有匹配的记录
        Entry entry = entries.get(0);
        string memory user_name =  entry.getString("username");
        string memory password_ =  entry.getString("password");
        return (user_name , password_);
    }
    function selectAll(string memory _id) public view  returns(string[] memory, string[] memory, string[] memory){
        Table table = tf.openTable(TABLE_NAME);
        Condition condition = table.newCondition();
        Entries entries = table.select(_id,condition);
        string[] memory ids = new string[](uint256(entries.size()));
        string[] memory usernames = new string[](uint256(entries.size()));
        string[] memory passwords = new string[](uint256(entries.size()));
        for(int256 i = 0; i < entries.size(); ++i){
            Entry entry = entries.get(i);
            ids[uint256(i)] = entry.getString("id");
            usernames[uint256(i)] = entry.getString("username");
            passwords[uint256(i)] = entry.getString("password");
        }
        return (ids , usernames , passwords);
    }
    function update(string memory _id , string memory _username , string memory _newUsername) public  returns(int256){
        Table table = tf.openTable(TABLE_NAME);
        Entry entry = table.newEntry();
        entry.set("username",_newUsername);
        Condition condition = table.newCondition();
        condition.EQ("username",_username);
        int256 count = table.update(_id, entry, condition); //返回修改的行数
        return count;
    }
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.4.25;

contract TableFactory {
    /**
     * brief 打开表,返回Table合约地址
     * param tableName 表的名称
     * return 返回Table的地址,当表不存在时,将会返回空地址即address(0x0)
     */
    function openTable(string) public constant returns (Table);  // 打开表

    /**
     * brief 创建表,返回是否成功
     * param tableName 表的名称
     * param key 表的主键名
     * param valueFields 表的字段名,多个字段名以英文逗号分隔
     * return 返回错误码,成功为0,错误则为负数
     */
    function createTable(string tableName,string key,string valueFields) public returns(int);
}

// 查询条件
contract Condition {
    //等于
    function EQ(string, int) public;
    function EQ(string, string) public;

    //不等于
    function NE(string, int) public;
    function NE(string, string)  public;

    //大于
    function GT(string, int) public;
    //大于或等于
    function GE(string, int) public;

    //小于
    function LT(string, int) public;
    //小于或等于
    function LE(string, int) public;

    //限制返回记录条数
    function limit(int) public;
    function limit(int, int) public;
}

// 单条数据记录
contract Entry {
    function getInt(string) public constant returns(int);
    function getAddress(string) public constant returns(address);
    function getBytes64(string) public constant returns(byte[64]);
    function getBytes32(string) public constant returns(bytes32);
    function getString(string) public constant returns(string);
    
    function set(string, int) public;
    function set(string, string) public;
    function set(string, address) public;
}

// 数据记录集
contract Entries {
    function get(int) public constant returns(Entry);
    function size() public constant returns(int);
}

// Table主类
contract Table {
    /**
     * brief 查询接口
     * param key 查询主键值
     * param cond 查询条件
     * return Entries合约地址,合约地址一定存在
     */
    function select(string key, Condition cond) public constant returns(Entries);
    /**
     * brief 插入接口
     * param key 插入主键值
     * param entry 插入字段值
     * return 插入影响的行数
     */
    function insert(string key, Entry entry) public returns(int);
    /**
     * brief 更新接口
     * param key 更新主键值
     * param entry 更新字段值
     *param cond 更新条件
     * return 更新影响的行数
     */
    function update(string key, Entry entry, Condition cond) public returns(int);
    /**
     * brief 删除接口
     * param key 删除的主键值
     * param cond 删除条件
     * return 删除影响的行数
     */
    function remove(string key, Condition cond) public returns(int);

    function newEntry() public constant returns(Entry);
    function newCondition() public constant returns(Condition);
}
最后更新 2023-08-18
评论 ( 2 )
OωO
隐私评论
  1. solidity so easy

    8个月前江苏省常州市回复
    1. 111
      8个月前江苏省常州市回复