在 Solidity 中,可以使用内建对象来访问区块信息和调用时的数据。
block.number:
返回当前区块的区块号。
block.timestamp(或 now):
返回当前区块的时间戳,表示自1970年1月1日以来的秒数。
block.difficulty:
返回当前区块的挖矿难度。
block.gaslimit:
返回当前区块的 gas 限额。
block.coinbase:
返回当前区块的矿工地址。
blockhash(uint blockNumber) returns (bytes32):
返回指定区块号的区块哈希。
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;
}
}
function func name(paramlist...) modifiers returns (returnlist...)
function 是函数声明的关键字。
func_name自定义函数名称,与我修习惯的函数命名规范没有区别。
paramlist 参数列表,可以0或多个参数,格式是: 参数类型参数名称
modifiers函数的修饰符,非常关键,我们后面要详细讨论。
returns 返回值关键字,看到s应该能想到可以同时返回多个值 returnlist 返回值类型列表。
// 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;
}
}
}
在值传递中,函数接收的是参数的值,而不是参数本身的引用。
当你将一个变量作为参数传递给函数时,函数内部得到的是该变量的副本,对副本的修改不会影响原始变量。
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
在引用传递中,函数接收参数的引用,即参数的地址或指针。
当你将一个变量作为引用传递给函数时,函数内部对参数的修改会影响原始变量。
在 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 类型,这将创建一个参数的副本,从而实现值传递。
在 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 语句,中断函数执行。
在被修饰的函数中,修饰符内的 _ 表示执行被修饰函数的代码。
接口是一种抽象的合约,只包含函数声明而不包含实现。它提供了一种约定,要求任何实现该接口的合约都必须提供指定的函数。
// 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 函数的具体实现,否则会导致编译错误。
继承是面向对象编程中的重要概念,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 {
// ...
}
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;
}
}
// 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 函数用于获取合约当前的余额。