首页 > 其他分享 >Solidity基本语法学习4

Solidity基本语法学习4

时间:2023-12-10 15:23:36浏览次数:31  
标签:function 学习 string Solidity contract called 语法 foo public

文档: https://solidity-by-example.org/
视频教程: https://www.youtube.com/watch?v=xv9OmztShIw&list=PLO5VPQH6OWdVQwpQfw9rZ67O6Pjfo6q-p

说明:

本文内容: Function Modifier, Events, Constructor, Inheritance, Shadowing Inherited State Variables, Calling Parent Contracts, Visibility, Interface

Function Modifier

Function Modifier是可以在函数调用之前和/或之后运行的代码. 可以类比AOP.

修饰符可用于:

  • 限制访问
  • 验证输入
  • 防止重入攻击. 简单来说重入攻击是利用智能合约的漏洞反复调用智能合约从而非法获利的一种攻击方式.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract FunctionModifier {
    // We will use these variables to demonstrate how to use
    // modifiers.
    address public owner;
    uint public x = 10;
    bool public locked;

    constructor() {
        // Set the transaction sender as the owner of the contract.
        owner = msg.sender;
    }

    // Modifier to check that the caller is the owner of
    // the contract.
    modifier onlyOwner() {
        require(msg.sender == owner, "Not owner");
        // Underscore is a special character only used inside
        // a function modifier and it tells Solidity to
        // execute the rest of the code.
        _;
    }

    // Modifiers can take inputs. This modifier checks that the
    // address passed in is not the zero address.
    modifier validAddress(address _addr) {
        require(_addr != address(0), "Not valid address");
        _;
    }

    // 调用过一次修改成别的地址之后, 就没法修改回来了. 会报错的. 
    function changeOwner(address _newOwner) public onlyOwner validAddress(_newOwner) {
        owner = _newOwner;
    }

    // Modifiers can be called before and / or after a function.
    // This modifier prevents a function from being called while
    // it is still executing.
    modifier noReentrancy() {
        require(!locked, "No reentrancy");

        locked = true;
        _;
        locked = false;
    }

    function decrement(uint i) public noReentrancy {
        x -= i;

        if (i > 1) {
            decrement(i - 1);
        }
    }
}

Events

events允许记录到以太坊区块链。
events的一些用例是:

  • 监听事件, 更新用户接口
  • 一种廉价的存储形式
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract Event {
    // Event declaration
    // Up to 3 parameters can be indexed.
    // Indexed parameters helps you filter the logs by the indexed parameter
    event Log(address indexed sender, string message);
    event AnotherLog();

    function test() public {
        emit Log(msg.sender, "Hello World!");
        emit Log(msg.sender, "Hello EVM!");
        emit AnotherLog();
    }
}

要到控制台去看日志输出.

img

Inheritance

继承关系.
Solidity支持多继承Contract可以通过使用is关键字继承其他合同。
要被子智能合约覆盖的函数必须声明为虚函数(virtual)。
要重写父函数的函数必须使用关键字override
继承顺序很重要。您必须按照从most base-likemost derived的顺序列出父合约。

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

contract A {
    function foo() virtual  public pure returns(string memory){
        return "A";
    }
}

contract B is A {
    function foo() virtual  override  public pure returns(string memory){
        return "B";
    }
}

contract C is A {
    // 如果没有儿子合约(D和E)继承C了的话,就不需要virtual了。
    function foo() override virtual public pure returns(string memory){
        return "C";
    }
}

contract D is B, C {
    function foo() public pure override(B, C) virtual returns(string memory){
        return super.foo();
    }
}

// 注意对比, D.foo()返回的是"C", 而E.foo()返回的是"B".  
// 关键在于继承顺序, 而非override里面的顺序. 
contract E is C, B {
    function foo() public pure override(B, C) virtual returns(string memory){
        return super.foo();
    }
}

// 但是要继承父和爷合约的话必须从左到右从大到小. 上面也说过了.
contract F is A, B {
    function foo() public pure override(A, B) virtual returns(string memory){
        return super.foo();
    }
}

Constructor

构造函数是可选函数,在智能合约创建时执行。

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

// Base contract X
contract X {
    string public name;

    constructor(string memory _name) {
        name = _name;
    }
}

// Base contract Y
contract Y {
    string public text;

    constructor(string memory _text) {
        text = _text;
    }
}

// There are 2 ways to initialize parent contract with parameters.

// Pass the parameters here in the inheritance list.
contract B is X("Input to X"), Y("Input to Y") {

}

contract C is X, Y {
    // Pass the parameters here in the constructor,
    // similar to function modifiers.
    constructor(string memory _name, string memory _text) X(_name) Y(_text) {}
}

// Parent constructors are always called in the order of inheritance
// regardless of the order of parent contracts listed in the
// constructor of the child contract.

// Order of constructors called:
// 1. X
// 2. Y
// 3. D
contract D is X, Y {
    constructor() X("X was called") Y("Y was called") {}
}

// Order of constructors called:
// 1. X
// 2. Y
// 3. E
contract E is X, Y {
    constructor() Y("Y was called") X("X was called") {}
}

Shadowing Inherited State Variables

意思是隐藏继承的状态变量. 与函数不同,状态变量state variable不能通过在子契约中重新声明来重写。让我们学习如何正确地重写继承的状态变量.

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

contract A {
    string public name = "Contract A";

    function getName() public view returns(string memory) {
        return name;
    }
}

// 0.6之后这种形式就被ban了. 
// contract B is A {
//     string public name = "Contract B";
// }

contract C is A {
    constructor() {
        name = "Contract C";
    }
}

Calling Parent Contracts

父契约可以直接调用,也可以使用关键字super调用。通过使用关键字super,将调用所有的直接父契约. 前面也已经演示过了.

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

/* Inheritance tree
   A
 /  \
B   C
 \ /
  D
*/

contract A {
    // This is called an event. You can emit events from your function
    // and they are logged into the transaction log.
    // In our case, this will be useful for tracing function calls.
    event Log(string message);

    function foo() public virtual {
        emit Log("A.foo called");
    }

    function bar() public virtual {
        emit Log("A.bar called");
    }
}

contract B is A {
    function foo() public virtual override {
        emit Log("B.foo called");
        A.foo();
    }

    function bar() public virtual override {
        emit Log("B.bar called");
        super.bar();
    }
}

contract C is A {
    function foo() public virtual override {
        emit Log("C.foo called");
        A.foo();
    }

    function bar() public virtual override {
        emit Log("C.bar called");
        super.bar();
    }
}

contract D is B, C {
    // Try:
    // - Call D.foo and check the transaction logs.
    //   Although D inherits A, B and C, it only called C and then A.
    // - Call D.bar and check the transaction logs
    //   D called C, then B, and finally A.
    //   Although super was called twice (by B and C) it only called A once.

    function foo() public override(B, C) {
        super.foo();
    }

    function bar() public override(B, C) {
        super.bar();
    }
}

// 顺序是C -> B -> A, 也是有讲究的. 
// D声明写成`D is C, B`就是B, C, A顺序了
[
	{
		"from": "0x540d7E428D5207B30EE03F2551Cbb5751D3c7569",
		"topic": "0xcf34ef537ac33ee1ac626ca1587a0a7e8e51561e5514f8cb36afa1c5102b3bab",
		"event": "Log",
		"args": {
			"0": "C.bar called",
			"message": "C.bar called"
		}
	},
	{
		"from": "0x540d7E428D5207B30EE03F2551Cbb5751D3c7569",
		"topic": "0xcf34ef537ac33ee1ac626ca1587a0a7e8e51561e5514f8cb36afa1c5102b3bab",
		"event": "Log",
		"args": {
			"0": "B.bar called",
			"message": "B.bar called"
		}
	},
	{
		"from": "0x540d7E428D5207B30EE03F2551Cbb5751D3c7569",
		"topic": "0xcf34ef537ac33ee1ac626ca1587a0a7e8e51561e5514f8cb36afa1c5102b3bab",
		"event": "Log",
		"args": {
			"0": "A.bar called",
			"message": "A.bar called"
		}
	}
]

Visibility

函数和状态变量必须声明它们是否可以被其他智能合约访问
函数可以声明为:

  • public---任何合约和账户都可以调用.
  • private---只能在本合约内用.
  • internal---同一合约和儿子合约可以调用.
  • external---仅其他契约和帐户可以调用
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;

contract Base {
    // Private function can only be called
    // - inside this contract
    // Contracts that inherit this contract cannot call this function.
    function privateFunc() private pure returns(string memory){
        return "private function called";
    }
    function testPrivateFunc() pure public returns(string memory){
        return string(abi.encodePacked(privateFunc(), "test"));
    }
    // Internal function can be called
    // - inside this contract
    // - inside contracts that inherit this contract
    function internalFunc() internal pure returns(string memory){
        return "internal function called";
    }
    function testInternalFunc() public pure virtual returns(string memory){
        return string(abi.encodePacked(internalFunc(), "test"));
    }
    function publicFunc() public pure returns(string memory){
        return "public function called";
    }
    // External functions can only be called
    // - by other contracts and accounts
    function externalFunc() external pure returns(string memory){
        return "external function called";
    }

    // This function will not compile since we're trying to call
    // an external function here.
    // function testExternalFunc() public pure returns (string memory) {
    //     // 报错: DeclarationError: Undeclared identifier.
    //     return externalFunc();
    // }

    string private privateVar = "my private variable";
    string internal internalVar = "my internal variable";
    string public publicVar = "my public variable";
    // State variables cannot be external so this code won't compile.
    // 报错: ParserError: Expected identifier but got 'external'
    // string external externalVar = "my external variable";
}

contract Child is Base {
    // Inherited contracts do not have access to private functions
    // and state variables.
    // 报错: DeclarationError: Undeclared identifier.
    // function testPrivateFunc() public pure returns (string memory) {
    //     return privateFunc();
    // }
    function testInternalFunc() public pure override returns(string memory) {
        return string(abi.encodePacked(internalFunc(), " child test"));
    }
}

结果
注意这些函数都是用public或者external修饰的.

Interface

可以通过声明接口interface与其他合约进行交互。
interface特点:

  • 没有具体实现
  • 可以从其他接口继承
  • 所有声明的功能必须是external修饰的
  • 无法声明构造函数
  • 无法声明状态变量
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;

// Counter合约,用于计数
contract Counter {
    uint public count;

    // 增加计数的函数
    function increment() external {
        count += 1;
    }
}

// 计数器接口,定义了获取计数和增加计数的函数
interface ICounter {
    function count() external view returns (uint);
    function increment() external;
}

// MyContract合约,包含与ICounter合约交互的函数
contract MyContract {
    // 通过传递的_counter地址调用ICounter合约的increment函数
    function incrementCounter(address _counter) external {
        ICounter(_counter).increment();
    }

    // 通过传递的_counter地址调用ICounter合约的count函数
    function getCount(address _counter) external view returns(uint){
        return ICounter(_counter).count();
    }
}

// UniswapV2Factory接口,用于获取Uniswap交易对的地址
interface UniswapV2Factory {
    function getPair(
        address tokenA,
        address tokenB
    ) external view returns(address pair);
}

// UniswapV2Pair接口,用于获取Uniswap交易对的储备量信息
interface UniswapV2Pair {
    function getReserves()
        external 
        view 
        returns(uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
}

// UniswapExample合约,用于获取Uniswap交易对的储备量信息
contract UniswapExample {
    // Uniswap工厂合约地址
    address private factory = 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f;
    // DAI代币地址
    address private dai = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
    // WETH代币地址
    address private weth = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;

    // 获取Uniswap交易对的储备量信息
    function getTokenReserves() external view returns (uint, uint){
        // 获取DAI-WETH交易对地址
        address pair = UniswapV2Factory(factory).getPair(dai, weth);
        // 调用UniswapV2Pair接口获取储备量信息
        (uint reserve0, uint reserve1, ) = UniswapV2Pair(pair).getReserves();
        return (reserve0, reserve1);
    }
}

uniswap还没研究过, 在Remix用的时候是报错的.
不过Counter的用法倒是清楚.
输出
注意填写的address, 是Counter合约的地址.
用右边Counter合约的地址执行左边的MyCounterincrementCounter, 会增加右边Counter合约的count.

标签:function,学习,string,Solidity,contract,called,语法,foo,public
From: https://www.cnblogs.com/joey-redfield/p/17892684.html

相关文章

  • 2023-2024-120232325《网络空间安全导论》第五周学习
    教材内容总结问题1.爬虫怎么制作:制作一个爬虫需要以下几个步骤:确定目标网站:首先确定需要爬取的目标网站,了解网站的结构和内容。选择合适的爬虫框架:根据自己的需求选择合适的爬虫框架,比如Python的Scrapy、BeautifulSoup、Selenium等。编写爬虫代码:根据目标网站的结构和内容......
  • 如何更好的学习Python
    学习python需要有哪些思维任何编程语言时,培养一些特定的思维方式是非常有帮助的。下面是一些学习Python所需的思维方式:问题解决思维:学会将问题分解成更小、更可管理的部分,然后逐步解决每个部分。善于提问,学会将问题清晰地表达,有助于获取更好的帮助和解决方案。抽象思维......
  • Linux-03shell语法3
    判断语句if…then形式类似于C/C++中的if-else语句。单层if命令格式:ifconditionthen语句1语句2...fi示例:a=3b=4if["$a"-lt"$b"]&&["$a"-gt2]thenecho${a}在范围内fi输出结果:3在范围内单层if-else命令格式ifcond......
  • 2023-2024-1 20231303 《计算机基础与程序设计》赵泊瑄第十一周学习总结
    2023-2024-120231303《计算机基础与程序设计》赵泊瑄第十一周学习总结作业信息这个作业属于哪个课程2023-2024-1-计算机基础与程序设计这个作业要求在哪里作业要求的链接https://i.cnblogs.com/posts/edit)这个作业的目标总结第十一周学习收获作业正文2023-......
  • 2023-2024-1 20232422《网络》第5周学习总结
    思维导图如下教材学习中的问题和解决过程问题:路由相关攻击是怎么进行的?解决:问Chatgpt得到如下答案:问题:缓存相关攻击的进行?解决:问chatgpt得到如下答案:缓存相关攻击通常是利用系统中的缓存机制,通过一些恶意手段来绕过或滥用缓存,从而导致安全漏洞或攻击。以下是......
  • 2023-2024-1 20232322 罗上林 《网络》第五周学习总结
    教材学习内容总结教材学习中的问题和解决过程-问题一:对信息内容安全威胁的来源不知道-问题一解决方案:-问题二:对信息内容过滤不理解-问题二解决方案:基于AI的学习参考资料《网络空间安全导论》网络空间安全导论书单......
  • 2023-2024-1 20232310 《网络空间安全导论》 第五章学习
    教材学习内容总结教材学习中的问题和解决过程问题1:难以理解非结构信息的结构化解决方法:通过询问ChatGPT学习了将非结构信息结构化的几种方法,进一步理解了非结构信息的结构化过程问题2:如何基于网络交互重构机制实现需要身份认证的动态网页发布信息获取解决方法:问gpt......
  • Linux-03shell语法-expr&read&echo&printf&test[]
    表达式运算命令expr概述expr命令用于求表达式的值,格式为:expr表达式表达式说明:用空格隔开每一项用反斜杠放在sh特定的字符前面(发现表达式运行错误时,可以试试转义)对包含空格和其他特殊字符的字符串要用引号括起来expr会在stdout中输出结果。如果为逻辑关系表达式,则结......
  • 2023-2024-1 20232407 《网络》第5周学习总结
    教材学习内容总结教材学习中的问题和解决过程问题1:什么是sybil攻击?问题1解决方案:询问chatgpt问题2:除计算机技术外,还有哪些领域需要协同工作来更好地保证信息内容的安全问题2解决方案:询问chatgpt基于AI的学习感悟人工智能的发展也为内容安全提供了新的解决方案,比如基于......
  • 2023-2024-1 20231421 《计算机基础与程序设计》第十一周学习总结
    ------------恢复内容开始------------------------恢复内容开始------------------------恢复内容开始------------------------恢复内容开始------------------------恢复内容开始------------------------恢复内容开始------------------------恢复内容开始------------......