【Solidity】基本语法

1. 内建对象

在 Solidity 中,可以使用内建对象来访问区块信息和调用时的数据。

1.1 区块信息

block.number:

       返回当前区块的区块号。

block.timestamp(或 now):

    返回当前区块的时间戳,表示自1970年1月1日以来的秒数。

block.difficulty:

    返回当前区块的挖矿难度。

block.gaslimit:

    返回当前区块的 gas 限额。

block.coinbase:

    返回当前区块的矿工地址。

blockhash(uint blockNumber) returns (bytes32):

    返回指定区块号的区块哈希。

1.2 调用时数据

msg.sender:

返回当前消息的发送者(调用者)地址。

msg.value:

返回当前消息附带的wei的数量。(以太坊激励体系内最小的虚拟数字货币单位)

msg.data:

返回完整的调用数据,包括函数选择器和参数。

msg.sig:

返回调用数据中的函数选择器。

msg.gas:

返回当前消息的 gas 余额
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract BlockInfoExample {
    
    
    //world state
    address public caller;
    bytes32 public hash;
    uint256 public random;

    //构造器
    constructor() {
    
    
        caller = msg.sender;
        hash = blockhash(0);
        random = uint256(keccak256(abi.encode(block.timestamp,caller,hash))) % 100;
    }
}

在这里插入图片描述

2. 函数

function func name(paramlist...) modifiers returns (returnlist...)

function 是函数声明的关键字。
func_name自定义函数名称,与我修习惯的函数命名规范没有区别。
paramlist 参数列表,可以0或多个参数,格式是: 参数类型参数名称
modifiers函数的修饰符,非常关键,我们后面要详细讨论。
returns 返回值关键字,看到s应该能想到可以同时返回多个值 returnlist 返回值类型列表。

2.1 修饰符

  1. public:函数标记为公共,可以从合同内部和外部访问。这是默认的访问修饰符。
  2. internal:函数标记为内部,只能从合同内部或继承自合同的合同中访问。外部合同无法调用这样的函数。
  3. external:函数标记为外部,只能从外部合同或外部交易中访问。这通常用于合同接口和外部交互。
  4. private:函数标记为私有,只能在合同内部访问。外部合同和外部交易无法访问私有函数。
  5. pure:函数声明为“纯函数”,表示它不会访问合同状态或存储变量,也不会发送交易或调用其他合同。这提供了额外的安全性和优化机会。
  6. view:函数声明为“查看函数”,表示它不会修改合同状态,但可以访问合同状态或存储变量。这用于读取数据而不写入。
  7. payable:函数声明为“可付费函数”,表示它可以接受以太币(ETH)或其他代币的支付。这通常用于接受转账或在交易中接收资金。
  8. constant(已弃用):在Solidity 0.4.x版本中使用,与view 类似,表示函数不会修改合同状态。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract FactorialExample {
    
    
    // 计算阶乘的函数
    function factorial(uint n) public pure returns (uint) {
    
    
        if (n == 0 || n == 1) {
    
    
            return 1;
        } else {
    
    
            uint result = 1;
            for (uint i = 2; i <= n; i++) {
    
    
                result *= i;
            }
            return result;
        }
    }
}

在这里插入图片描述

3. 值传递和引用传递

3.1 值传递

在值传递中,函数接收的是参数的值,而不是参数本身的引用。
当你将一个变量作为参数传递给函数时,函数内部得到的是该变量的副本,对副本的修改不会影响原始变量。
Solidity 中的基本数据类型(如整数、布尔值等)通常是值传递。

function modifyValue(uint x) public pure returns (uint) {
    
    
    x = x * 2;
    return x;
}

uint originalValue = 10;
uint modifiedValue = modifyValue(originalValue);
// originalValue 仍然是 10,modifiedValue 是 20

3.2 引用传递

在引用传递中,函数接收参数的引用,即参数的地址或指针。
当你将一个变量作为引用传递给函数时,函数内部对参数的修改会影响原始变量。
在 Solidity 中,复杂数据类型(如数组、结构体、映射等)通常是引用传递。

struct Person {
    
    
    string name;
    uint age;
}

function modifyPersonAge(Person storage person) internal {
    
    
    person.age = person.age + 1;
}

Person storage alice = Person("Alice", 25);
modifyPersonAge(alice);
// alice 的年龄变为 26

在 Solidity 中,需要注意以下几点:

对于基本数据类型,如果你希望在函数内部修改原始值,可以将参数声明为 storage 类型,这将使其成为引用传递。
对于复杂数据类型,如数组、结构体等,它们通常是引用传递的,无需显式声明。
如果不希望在函数内修改原始值,可以将参数声明为 memory 类型,这将创建一个参数的副本,从而实现值传递。

4. 自定义修饰符

在 Solidity 中,修饰符是一种用于修改函数行为的特殊类型。它可以被附加到函数定义之前,并在函数执行之前或之后执行额外的逻辑。自定义修饰符允许你封装常见的逻辑,以便在多个函数中共享和重用代码。

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

contract ModifierExample {
    
    
    address public owner;
    uint public value;

    // 自定义修饰符,用于限制只有合约所有者可以调用
    modifier onlyOwner() {
    
    
        require(msg.sender == owner, "Not the owner");
        _; // 执行被修饰函数的代码
    }

    // 自定义修饰符,用于限制值的范围
    modifier validValue(uint newValue) {
    
    
        require(newValue > 0 && newValue <= 100, "Invalid value");
        _; // 执行被修饰函数的代码
    }

    // 构造函数,初始化合约所有者
    constructor() {
    
    
        owner = msg.sender;
    }

    // 使用修饰符限制只有合约所有者可以设置值
    function setValue(uint newValue) public onlyOwner validValue(newValue) {
    
    
        value = newValue;
    }

    // 使用修饰符限制只有合约所有者可以获取值
    function getValue() public view onlyOwner returns (uint) {
    
    
        return value;
    }
}

在这个示例合约中:

onlyOwner 修饰符用于限制只有合约所有者可以调用被修饰的函数。如果调用者不是合约所有者,将触发 require 语句,中断函数执行。
validValue 修饰符用于限制设置值时的范围。如果传入的值不在指定范围内,将触发 require 语句,中断函数执行。
在被修饰的函数中,修饰符内的 _ 表示执行被修饰函数的代码。

5. 接口与继承

5.1 接口

接口是一种抽象的合约,只包含函数声明而不包含实现。它提供了一种约定,要求任何实现该接口的合约都必须提供指定的函数。

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

// 定义一个简单的接口
interface MyInterface {
    
    
    function myFunction() external;
}

// 实现接口的合约
contract MyImplementation is MyInterface {
    
    
    // 实现接口中的函数
    function myFunction() external override {
    
    
        // 具体实现逻辑
    }
}

在这个示例中:

MyInterface 是一个简单的接口,定义了一个名为 myFunction 的函数。
MyImplementation 合约使用 is MyInterface 表示它实现了 MyInterface 接口。
在 MyImplementation 合约中,你需要提供 myFunction 函数的具体实现,否则会导致编译错误。

5.2 继承

继承是面向对象编程中的重要概念,Solidity 也支持合约之间的继承关系。通过继承,合约可以从其他合约中继承状态变量和函数,实现代码的重用和组织。合约使用 is 关键字继承其他合约。通过这种方式,子合约可以继承父合约的状态变量和函数。

contract BaseContract {
    
    
    uint public baseValue;

    function baseFunction() public {
    
    
        // ...
    }
}

contract DerivedContract is BaseContract {
    
    
    uint public derivedValue;

    function derivedFunction() public {
    
    
        // ...可以重写
    }
}

此外,一个合约可以继承多个合约,通过逗号分隔。

contract A {
    
    
    // ...
}

contract B {
    
    
    // ...
}

contract C is A, B {
    
    
    // ...
}

6. receive函数

receive 函数是 Solidity 合约中的一个特殊函数,用于接收以太币。当一个合约接收代币时,如果合约没有定义 receive 函数,那么合约将拒绝接收任何代币。

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

contract ReceiveFunctionExample {
    
    
    // receive 函数用于接收
    receive() external payable {
    
    
        // 处理接收到
        // 注意:在这里可以执行任何逻辑,如记录事件、更新状态等
    }

    // 获取合约余额的函数
    function getContractBalance() public view returns (uint) {
    
    
        return address(this).balance;
    }
}

7. 合约模拟充值和提现

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

contract Wallet {
    
    
    address public owner;
    uint public balance;

    // 事件用于记录充值和提现操作
    event Deposit(address indexed depositor, uint amount);
    event Withdrawal(address indexed recipient, uint amount);

    // 仅合约所有者可调用的修饰符
    modifier onlyOwner() {
    
    
        require(msg.sender == owner, "Not the owner");
        _;
    }

    // 仅合约所有者可充值的修饰符
    modifier onlyOwnerOrDeposit() {
    
    
        require(msg.sender == owner || msg.value > 0, "Not the owner or invalid deposit");
        _;
    }

    // 构造函数,初始化合约所有者
    constructor() {
    
    
        owner = msg.sender;
    }

    // 充值函数
    receive() external payable onlyOwnerOrDeposit {
    
    
        balance += msg.value;
        emit Deposit(msg.sender, msg.value);
    }

    // 提现函数
    function withdraw(uint amount) public onlyOwner {
    
    
        require(amount <= balance, "Insufficient balance");
        payable(owner).transfer(amount);
        balance -= amount;
        emit Withdrawal(owner, amount);
    }

    // 获取合约余额的函数
    function getBalance() public view returns (uint) {
    
    
        return balance;
    }
}

在这个合约中:

receive 函数用于接收充值。通过修饰符 onlyOwnerOrDeposit,我们确保只有合约所有者或有有效转账的地址可以调用该函数。充值后,会触发 Deposit 事件记录充值操作。

withdraw 函数用于提现。只有合约所有者可以调用该函数。提现时,使用 transfer 函数将指定金额转账给合约所有者,同时更新合约余额并触发 Withdrawal 事件。

getBalance 函数用于获取合约当前的余额。

猜你喜欢

转载自blog.csdn.net/qq_45875349/article/details/134315280