Solidity基本语法学习4

如果你不相信努力和时光,那么成果就会是第一个选择辜负你的。不要去否定你自己的过去,也不要用你的过去牵扯你现在的努力和对未来的展望。不是因为拥有希望你才去努力,而是去努力了,你才有可能看到希望的光芒。Solidity基本语法学习4,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

文档: 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.

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之家整理,本文链接:https://www.bmabk.com/index.php/post/199800.html

(0)
小半的头像小半

相关推荐

极客之家——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!