feat: v1.0.0 祀梦笔记:从 0 到 1 的数字化花园建设
This commit is contained in:
21
docs/notes/programming/solidity/README.md
Normal file
21
docs/notes/programming/solidity/README.md
Normal file
@@ -0,0 +1,21 @@
|
||||
---
|
||||
title: Solidity 学习笔记
|
||||
description: 记录Solidity智能合约开发的学习心得和基础知识
|
||||
createTime: 2025/09/28 19:39:00
|
||||
permalink: /programming/solidity/
|
||||
---
|
||||
|
||||
## Solidity 智能合约概述
|
||||
|
||||
Solidity 是一种用于编写智能合约的静态类型编程语言,它运行在以太坊虚拟机(EVM)上。
|
||||
|
||||
## 推荐的资料
|
||||
推荐的编辑器
|
||||
|
||||
Remix IDE:[https://remix.ethereum.org/](https://remix.ethereum.org/)
|
||||
|
||||
有在线版本,也可以下载之后使用,而且可以通过 Docker 部署,很方便
|
||||
|
||||
Solidity 学习资料:
|
||||
- [Solidity 官方文档](https://docs.soliditylang.org/zh-cn/latest/index.html)
|
||||
- [cryptozombies](https://cryptozombies.io/zh/course)
|
||||
88
docs/notes/programming/solidity/basic-syntax.md
Normal file
88
docs/notes/programming/solidity/basic-syntax.md
Normal file
@@ -0,0 +1,88 @@
|
||||
---
|
||||
title: Solidity 基础语法与数据类型
|
||||
createTime: 2025/09/22 10:30:00
|
||||
permalink: /programming/solidity/basic-syntax/
|
||||
---
|
||||
|
||||
# Solidity 基础语法与数据类型~(≧∇≦)ノ
|
||||
|
||||
## Solidity 文件基础框架
|
||||
|
||||
Solidity 文件的基础框架通常包含以下几个部分:
|
||||
|
||||
1. **SPDX 许可证标识符 (SPDX License Identifier)**:为了避免法律问题和明确智能合约的开源许可证,建议在合约的开头添加 SPDX 许可证标识符。
|
||||
2. **Solidity 版本声明 (Pragma)**:这会告诉编译器您希望使用哪个版本的 Solidity 来编译您的代码。
|
||||
3. **ABI编码编译指示**:通过`pragma abicoder v1`或`pragma abicoder v2`来指定ABI编码版本。
|
||||
4. **合约定义 (Contract Definition)**:这是您编写智能合约代码的主体部分。
|
||||
|
||||
下面是一个基础的 Solidity 文件框架示例,您可以直接使用:
|
||||
|
||||
```solidity
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
contract MyContract {
|
||||
// 在这里编写您的合约代码
|
||||
}
|
||||
```
|
||||
|
||||
## Solidity 基础数据类型
|
||||
|
||||
- 布尔类型(bool):值为 `true` 和 `false`
|
||||
- 运算符:`!`(逻辑非)、`&&`(逻辑与)、`||`(逻辑或)、`==`(等于)、`!=`(不等于)
|
||||
- 整形(int/uint):
|
||||
- 有符号整数(int):可以表示负数和零。
|
||||
- 无符号整数(uint):只能表示非负数。
|
||||
- 整数类型可以是8位、16位、32位、64位或256位。(只要是八的倍数都可以)
|
||||
- 例如:`int8`、`uint256`等。
|
||||
- 运算符:
|
||||
- 比较运算符:`<=`,`<`,`>=`,`>`
|
||||
- 位运算符:`&`(按位与)、`|`(按位或)、`^`(按位异或)、`~`(按位取反)
|
||||
- 移位运算符:`<<`(左移)、`>>`(右移)
|
||||
- 算数运算符:`+`(加法)、`-`(减法)、`*`(乘法)、`/`(除法)、`%`(取余)
|
||||
- 对于一个整数类型`X`,可以使用`type(X).min`和`type(X).max`来获取其最小值和最大值。
|
||||
|
||||
|
||||
|
||||
## Solidity 基础语法结构
|
||||
|
||||
## Solidity 基础操作
|
||||
|
||||
### 导入其他源文件
|
||||
|
||||
Solidity 支持导入其他源文件,使用`import`语句。例如:
|
||||
|
||||
```solidity
|
||||
import "filename.sol";
|
||||
```
|
||||
|
||||
这将导入`filename.sol`文件中的合约代码。
|
||||
|
||||
还可以创建一个新的全局符号
|
||||
|
||||
```solidity
|
||||
import * as NewName from "filename.sol"
|
||||
```
|
||||
|
||||
这将创建一个新的全局符号`NewName`,您可以在合约中使用它来引用`filename.sol`文件中的合约,例如`NewName.ContractName`。
|
||||
|
||||
还可以在导入的同时重命名符号:`import {symbol1 as alias, symbol2} from "filename";`
|
||||
|
||||
### 注释
|
||||
|
||||
Solidity 支持两种类型的注释:
|
||||
|
||||
1. **行注释**:使用`//`来注释单行代码。
|
||||
2. **块注释**:使用`/*`和`*/`来注释多行代码。
|
||||
|
||||
例如:
|
||||
|
||||
```solidity
|
||||
// 这是一个行注释
|
||||
|
||||
/*
|
||||
这是一个
|
||||
块注释
|
||||
*/
|
||||
```
|
||||
922
docs/notes/programming/solidity/case-analysis.md
Normal file
922
docs/notes/programming/solidity/case-analysis.md
Normal file
@@ -0,0 +1,922 @@
|
||||
---
|
||||
title: 十份代码实例
|
||||
createTime: 2025/10/10 11:07:11
|
||||
permalink: /programming/solidity/analysis/case-analysis/
|
||||
password: simeng
|
||||
---
|
||||
|
||||
## 题目一
|
||||
|
||||
原题:
|
||||
|
||||
1.食品信息(FoodInfoItem)的接口编码
|
||||
|
||||
(1)编写食品信息实体的接口,完成可溯源食品信息初始化,实现可追溯的原始生产商食品信息上链功能
|
||||
|
||||
(2)编写分销商食品上链信息接口,根据食品溯源智能合约地址获取分销商上链食品的信息
|
||||
|
||||
(3)编写超市进行食品上链信息的接口,根据食品溯源智能合约地址获取超市上链食品信息。
|
||||
|
||||
2.食品溯源(Trace)的接口编码
|
||||
|
||||
(1)编写食品溯源智能合约生产商Producer添加食品接口,必须生产商才能添加可溯源的食品,实现溯源功能
|
||||
|
||||
(2)编写食品溯源智能合约分销商Distributor添加食品接口,必须分销商才能添加可溯源的食品,实现溯源功能
|
||||
|
||||
(3)编写食品溯源智能合约超市Retailer添加食品接口,必须超市才能添加可溯源的食品,实现溯源功能
|
||||
|
||||
3.角色(Role)管理的接口编码
|
||||
|
||||
(1)编写食品溯源增加角色接口,必须是未增加的角色才能被添加,实现添加角色的功能
|
||||
|
||||
(2)编写食品溯源移除角色接口,必须是已增加的角色才能被移除,实现移除角色的功能
|
||||
|
||||
(3)编写食品溯源角色授权接口,必须是授权的角色地址,实现角色权限管理功能
|
||||
|
||||
::: code-tabs
|
||||
@tab Distributor.sol
|
||||
```solidity
|
||||
pragma solidity ^0.4.25;
|
||||
|
||||
import "./Roles.sol";
|
||||
|
||||
//中间商角色
|
||||
contract Distributor {
|
||||
using Roles for Roles.Role;
|
||||
|
||||
event DistributorAdded(address indexed account);
|
||||
event DistributorRemoved(address indexed account);
|
||||
|
||||
Roles.Role private _distributors;
|
||||
|
||||
constructor (address distributor ) public {
|
||||
_addDistributor(distributor);
|
||||
}
|
||||
|
||||
modifier onlyDistributor() {
|
||||
require(isDistributor(msg.sender), "DistributorRole: caller does not have the Distributor role");
|
||||
_;
|
||||
}
|
||||
|
||||
function isDistributor(address account) public view returns (bool) {
|
||||
return _distributors.has(account);
|
||||
}
|
||||
|
||||
function addDistributor(address account) public onlyDistributor {
|
||||
_addDistributor(account);
|
||||
}
|
||||
|
||||
function renounceDistributor() public {
|
||||
_removeDistributor(msg.sender);
|
||||
}
|
||||
|
||||
function _addDistributor(address account) internal {
|
||||
_distributors.add(account);
|
||||
emit DistributorAdded(account);
|
||||
}
|
||||
|
||||
function _removeDistributor(address account) internal {
|
||||
_distributors.remove(account);
|
||||
emit DistributorRemoved(account);
|
||||
}
|
||||
}
|
||||
```
|
||||
@tab FoodInfoItem.sol
|
||||
```solidity
|
||||
pragma solidity >=0.4.22 <0.5.0;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
//食品信息管理合约
|
||||
// 1. 保存食品基本信息:时间戳(流转过程中),用户名(流转过程中),用户地址信息(流转过程中),食品质量(流转过程中),食物名称,当前用户名称,质量,状态.
|
||||
// 2. 对食品基本信息进行初始化
|
||||
// 3. 实现两个方法:中间商添加食品信息;超市添加食品信息
|
||||
// 4. 实现显示食品信息的方法
|
||||
|
||||
contract FoodInfoItem{
|
||||
uint[] _timestamp;//①保存食品流转过程中各个阶段的时间戳
|
||||
string[] _traceName;//②保存食品流转过程各个阶段的用户名
|
||||
address[] _traceAddress;//③保存食品流转过程各个阶段的用户地址信息(和用户一一对应)
|
||||
uint8[] _traceQuality;//④保存食品流转过程中各个阶段的质量
|
||||
string _name;//⑤食品名称
|
||||
string _currentTraceName;//⑥当前用户名称
|
||||
uint8 _quality;//⑦质量(0=优质 1=合格 2=不合格)
|
||||
uint8 _status;//⑧状态(0:生产 1:分销 2:出售)
|
||||
address _owner;//⑨初始化owner
|
||||
|
||||
constructor (string name, string traceName, uint8 quality, address producer) public {
|
||||
_timestamp.push(now);
|
||||
_traceName.push(traceName);
|
||||
_traceAddress.push(producer);
|
||||
_traceQuality.push(quality);
|
||||
_name = name;
|
||||
_currentTraceName = traceName;
|
||||
_quality = quality;
|
||||
_status = 0;
|
||||
_owner = msg.sender;
|
||||
}
|
||||
|
||||
function addTraceInfoByDistributor(string traceName,address distributor, uint8 quality) public returns(bool) {
|
||||
require(_status == 0 , "status must be producing");
|
||||
require(_owner == msg.sender,"only trace contract can invoke")
|
||||
//②
|
||||
_timestamp.push(now);
|
||||
_traceName.push(traceName);
|
||||
_currentTraceName = traceName;
|
||||
_traceAddress.push(distributor);
|
||||
_quality = quality;
|
||||
//③
|
||||
//④
|
||||
_traceQuality.push(_quality);
|
||||
_status = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
function addTraceInfoByRetailer(string traceName,address retailer, uint8 quality) public returns(bool) {
|
||||
require(_status == 1 , "status must be distributing");
|
||||
require(_owner == msg.sender,"only trace contract can invoke")
|
||||
//②
|
||||
_timestamp.push(now);
|
||||
_traceName.push(traceName);
|
||||
_currentTraceName = traceName;
|
||||
_traceAddress.push(retailer);
|
||||
_quality = quality;
|
||||
//③
|
||||
//④
|
||||
_traceQuality.push(_quality);
|
||||
_status = 2;
|
||||
return true;
|
||||
}
|
||||
function getTraceInfo() public constant returns(uint[], string[], address[], uint8[]) {
|
||||
return(_timestamp, _traceName, _traceAddress, _traceQuality);
|
||||
}
|
||||
|
||||
function getFood() public constant returns(uint, string, string, string, address, uint8) {
|
||||
return(_timestamp[0], _traceName[0], _name, _currentTraceName, _traceAddress[0], _quality);
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
@tab Producer.sol
|
||||
```solidity
|
||||
pragma solidity ^0.4.25;
|
||||
|
||||
import "./Roles.sol";
|
||||
|
||||
//生产者角色
|
||||
contract Producer {
|
||||
using Roles for Roles.Role;
|
||||
|
||||
event ProducerAdded(address indexed account);
|
||||
event ProducerRemoved(address indexed account);
|
||||
|
||||
Roles.Role private _producers;
|
||||
|
||||
constructor (address producer) public {
|
||||
_addProducer(producer);
|
||||
}
|
||||
|
||||
modifier onlyProducer() {
|
||||
require(isProducer(msg.sender), "ProducerRole: caller does not have the Producer role");
|
||||
_;
|
||||
}
|
||||
|
||||
function isProducer(address account) public view returns (bool) {
|
||||
return _producers.has(account);
|
||||
}
|
||||
|
||||
function addProducer(address account) public onlyProducer {
|
||||
_addProducer(account);
|
||||
}
|
||||
|
||||
function renounceProducer() public {
|
||||
_removeProducer(msg.sender);
|
||||
}
|
||||
|
||||
function _addProducer(address account) internal {
|
||||
_producers.add(account);
|
||||
emit ProducerAdded(account);
|
||||
}
|
||||
|
||||
function _removeProducer(address account) internal {
|
||||
_producers.remove(account);
|
||||
emit ProducerRemoved(account);
|
||||
}
|
||||
}
|
||||
```
|
||||
@tab Retailer.sol
|
||||
```solidity
|
||||
pragma solidity ^0.4.25;
|
||||
|
||||
import "./Roles.sol";
|
||||
|
||||
//零售商角色(超市)
|
||||
contract Retailer {
|
||||
using Roles for Roles.Role;
|
||||
|
||||
event RetailerAdded(address indexed account);
|
||||
event RetailerRemoved(address indexed account);
|
||||
|
||||
Roles.Role private _retailers;
|
||||
|
||||
constructor (address retailer) public {
|
||||
_addRetailer(retailer);
|
||||
}
|
||||
|
||||
modifier onlyRetailer() {
|
||||
require(isRetailer(msg.sender), "RetailerRole: caller does not have the Retailer role");
|
||||
_;
|
||||
}
|
||||
|
||||
function isRetailer(address account) public view returns (bool) {
|
||||
return _retailers.has(account);
|
||||
}
|
||||
|
||||
function addRetailer(address account) public onlyRetailer {
|
||||
_addRetailer(account);
|
||||
}
|
||||
|
||||
function renounceRetailer() public {
|
||||
_removeRetailer(msg.sender);
|
||||
}
|
||||
|
||||
function _addRetailer(address account) internal {
|
||||
_retailers.add(account);
|
||||
emit RetailerAdded(account);
|
||||
}
|
||||
|
||||
function _removeRetailer(address account) internal {
|
||||
_retailers.remove(account);
|
||||
emit RetailerRemoved(account);
|
||||
}
|
||||
}
|
||||
```
|
||||
@tab Roles.sol
|
||||
```solidity
|
||||
pragma solidity ^0.4.25;
|
||||
//角色库(管理所有角色地址)
|
||||
// 1. 实现增加角色地址
|
||||
// 2. 移除角色地址
|
||||
// 3. 判断角色地址是否被授权
|
||||
library Roles {
|
||||
struct Role {
|
||||
mapping (address => bool) bearer;
|
||||
}
|
||||
function add(Role storage role, address account) internal {
|
||||
require(!has(role, account), "Roles: account already has role");
|
||||
role.bearer[account] = true;
|
||||
}
|
||||
function remove(Role storage role, address account) internal {
|
||||
require(has(role, account), "Roles: account does not have role");
|
||||
role.bearer[account] = false;
|
||||
}
|
||||
function has(Role storage role, address account) internal returns (bool) {
|
||||
require(account != address(0), "Roles: account is the zero address");
|
||||
return role.bearer[account];
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
@tab Trace.sol
|
||||
```solidity
|
||||
pragma solidity >=0.4.22 <0.7.0;
|
||||
pragma experimental ABIEncoderV2;
|
||||
import "./FoodInfoItem.sol";
|
||||
import "./Distributor.sol";
|
||||
import "./Producer.sol";
|
||||
import "./Retailer.sol";
|
||||
|
||||
//食品溯源合约(负责具体食品溯源信息的生成)
|
||||
// 1.实现生产食品的方法(新建食品信息)
|
||||
// 2.实现食品分销过程中增加溯源信息的接口
|
||||
// 3.实现食品出售过程中增加溯源信息的接口
|
||||
// 4.实现获取食品溯源信息接口
|
||||
|
||||
contract Trace is Producer, Distributor, Retailer{
|
||||
|
||||
mapping (uint256 => address) foods;//食品溯源id到具体食品溯源合约的映射表
|
||||
uint[] foodList;
|
||||
|
||||
//构造函数
|
||||
constructor(address producer, address distributor, address retailer)
|
||||
public Producer(producer)
|
||||
Distributor(distributor)
|
||||
Retailer(retailer){
|
||||
|
||||
}
|
||||
//生成食品溯源信息接口
|
||||
//只有Producer能调用
|
||||
//name 食品名称
|
||||
//traceNumber 食品溯源id
|
||||
//traceName 当前用户名称
|
||||
//quality 当前食品质量
|
||||
function newFood(string name,uint256 traceNumber, string traceName, uint8 quality)
|
||||
public onlyProducer returns(uint)
|
||||
{
|
||||
require(foods[traceNumber] == address(0),"traceName already exist");//④
|
||||
FoodInfoItem food = new FoodInfoItem(name,traceName,quality,msg.sender);//⑤
|
||||
foods[traceNumber] = food;//⑥
|
||||
foodList.push(traceNumber);//⑦
|
||||
retur food;//⑧
|
||||
}
|
||||
|
||||
//食品分销过程中增加溯源信息的接口
|
||||
//只有Distributor能调用
|
||||
//traceNumber 食品溯源id
|
||||
//traceName 当前用户名称
|
||||
//quality 当前食品质量
|
||||
function addTraceInfoByDistributor(uint256 traceNumber,string traceName, uint8 quality)
|
||||
public onlyRetailer returns(bool) {
|
||||
require(foods[traceNumber] != address(0),"traceNumber does not exist");
|
||||
//③
|
||||
return FoodInfoItem(foods[traceNumber]).addTraceInfoByDistributor(traceName,msg.sender, quality);
|
||||
}
|
||||
|
||||
//食品出售过程中增加溯源信息的接口
|
||||
//只有Retailer能调用
|
||||
//traceNumber 食品溯源id
|
||||
//traceName 当前用户名称
|
||||
//quality 当前食品质量
|
||||
function addTraceInfoByRetailer(uint256 traceNumber,string traceName, uint8 quality)
|
||||
public onlyRetailer returns(bool) {
|
||||
require(foods[traceNumber != address(0)], "traceNumber does not exist");
|
||||
return FoodInfoItem(foods[traceNumber]).addTraceInfoByRetailer(traceName,msg.sender, quality);
|
||||
}
|
||||
|
||||
//获取食品溯源信息接口
|
||||
//string[] 保存食品流转过程中各个阶段的相关信息
|
||||
//address[] 保存食品流转过程各个阶段的用户地址信息(和用户一一对应)
|
||||
//uint8[] 保存食品流转过程中各个阶段的状态变化
|
||||
function getTraceInfo(uint256 traceNumber) public constant returns(uint[], string[], address[], uint8[]) {
|
||||
require(foods[traceNumber] != address(0), "traceNumber does not exist");
|
||||
return FoodInfoItem(foods[traceNumber]).getTraceInfo();
|
||||
}
|
||||
|
||||
function getFood(uint256 traceNumber) public constant returns(uint, string, string, string, address, uint8) {
|
||||
require(foods[traceNumber] != address(0), "traceNumber does not exist");
|
||||
return FoodInfoItem(foods[traceNumber]).getFood();
|
||||
}
|
||||
|
||||
function getAllFood() public constant returns (uint[]) {
|
||||
return foodList;
|
||||
}
|
||||
}
|
||||
```
|
||||
:::
|
||||
## 题目二
|
||||
|
||||
题目:
|
||||
|
||||
1.供应链金融实体信息编码(6分)
|
||||
|
||||
(1)编写供应链金融智能合约的实体接口,完成实体通用数据的初始化,实现企业和票据实体信息上链的功能;(2分)
|
||||
|
||||
(2)编写企业上链信息接口,实现供应链金融的企业信息上链;(2分)
|
||||
|
||||
(3)基于给定的智能合约代码以及注释,完成银行向企业交易的接口函数;(2分)
|
||||
|
||||
2.供应链金融公司与公司接口编码(6分)
|
||||
|
||||
(1)编写公司与公司之间进行交易的历史存证上链接口,实现公司与公司之间的交易功能;(2分)
|
||||
|
||||
(2)编写创建存证的接口,实现创建存证的功能;(2分)
|
||||
|
||||
(3)编写交易金额数量变化的接口,实现凭证交易双方资金的变化功能;(2分)
|
||||
|
||||
3.供应链金融公司与银行交易的接口编码(4分)
|
||||
|
||||
(1)编写公司与银行之间进行交易的历史存证上链接口,实现公司与银行之间的交易功能;(2分)
|
||||
|
||||
(2)编写创建存证的接口,实现创建存证的功能;(1分)
|
||||
|
||||
(3)编写交易金额数量变化的接口,实现凭证交易双方资金的变化功能;(1分)
|
||||
|
||||
::: code-tabs
|
||||
@tab xxx.sol
|
||||
```solidity
|
||||
```
|
||||
@tab xxx.sol
|
||||
```solidity
|
||||
```
|
||||
@tab xxx.sol
|
||||
```solidity
|
||||
```
|
||||
:::
|
||||
## 题目三
|
||||
|
||||
题目:
|
||||
|
||||
子任务2-2-1:太阳能板管理接口编码
|
||||
|
||||
1. 根据文档要求,编写太阳能板新增接口功能,必须将新增太阳能板数据存入指定表中,在存储完成后需触发后事件并返回存储与否的标识;
|
||||
|
||||
2. 根据文档要求,编写太阳能板修改接口,必须通过指定表修改完成数据更新,在完成更新后需触发事件并返回更新与否的标识。
|
||||
|
||||
子任务2-2-2:能源管理接口编码
|
||||
|
||||
1. 根据文档要求,编写能源新增接口功能,必须将新增能源数据存入指定表中,在存储完成后需触发后事件并返回存储与否的标识;
|
||||
|
||||
2. 根据文档要求,编写能源修改接口,必须通过指定表修改完成数据更新,在完成更新后需触发事件并返回更新与否的标识。
|
||||
|
||||
子任务2-2-3:合约部署和调用
|
||||
|
||||
1. 解决代码错误和警告,正确编译并部署合约,成功获取部署的合约地址和ABI;
|
||||
|
||||
2. 调用太阳能板查询合约接口,完整验证业务流程;
|
||||
|
||||
3. 调用能源查询合约接口,完整验证业务流程。
|
||||
|
||||
::: code-tabs
|
||||
@tab xxx.sol
|
||||
```solidity
|
||||
```
|
||||
@tab xxx.sol
|
||||
```solidity
|
||||
```
|
||||
@tab xxx.sol
|
||||
```solidity
|
||||
```
|
||||
:::
|
||||
## 题目四
|
||||
|
||||
原题:
|
||||
|
||||
1.编写原材料接口newMaterial,初始化原材料信息,返回合约地址,并实现原材料信息上链功能。
|
||||
|
||||
2.编写获取存原材料接口getMaterial,根据合约地址获取原材料信息
|
||||
|
||||
3.编写食品物流上链接口addLogistic,实现食品物流信息上链功能
|
||||
|
||||
4.编写获取食品物流信息的接口getLogistics,根据食品产品编号获取物流信息
|
||||
|
||||
::: code-tabs
|
||||
@tab Logistics.sol
|
||||
```solidity
|
||||
pragma solidity 0.6.10;
|
||||
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
contract Logistics {
|
||||
|
||||
struct LogisticsData {
|
||||
address cargo; //货物合约地址
|
||||
address orgin; //货物上级合约地址
|
||||
address destination; //货物下级合约地址
|
||||
string memo; //备注信息
|
||||
uint createdAt; //创建时间
|
||||
uint queryCount; //已查询次数
|
||||
}
|
||||
|
||||
LogisticsData[] private _logisticsData; //全部物流信息数组
|
||||
|
||||
uint public recordCount;//所有记录条数
|
||||
uint public queryCount;//所有查询次数
|
||||
|
||||
//可自行添加形参和返回值
|
||||
function addLogistics(address _cargo,address _orgin,【请补充】) public {
|
||||
//TODO:”请补充缺失代码
|
||||
}
|
||||
|
||||
//可自行添加形参和返回值
|
||||
function getLogisticsInfo(address _cargo) public returns(LogisticsData[] memory _cargoLogisticsData) {
|
||||
|
||||
//TODO:”请补充缺失代码
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
@tab Material.sol
|
||||
```solidity
|
||||
pragma solidity 0.6.10;
|
||||
|
||||
contract Material {
|
||||
struct Material {
|
||||
address owner;
|
||||
string name;
|
||||
string id;
|
||||
string memo;
|
||||
uint createdAt;
|
||||
bool exist;
|
||||
}
|
||||
|
||||
mapping(string => Material) public materials;
|
||||
//可自行添加形参和返回值
|
||||
function newMaterial(string memory _name,string memory _id,【请补充】) public {
|
||||
|
||||
//TODO:”请补充缺失代码
|
||||
}
|
||||
|
||||
|
||||
//可自行添加形参和返回值
|
||||
function getMaterial(string memory id) public view returns(【请补充】) {
|
||||
|
||||
//TODO:”请补充缺失代码
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
:::
|
||||
|
||||
参考答案:
|
||||
|
||||
::: code-tabs
|
||||
@tab Logistics.sol
|
||||
```solidity
|
||||
pragma solidity 0.6.10;
|
||||
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
contract Logistics {
|
||||
|
||||
struct LogisticsData {
|
||||
address cargo; //货物合约地址
|
||||
address orgin; //货物上级合约地址
|
||||
address destination; //货物下级合约地址
|
||||
string memo; //备注信息
|
||||
uint createdAt; //创建时间
|
||||
uint queryCount; //已查询次数
|
||||
}
|
||||
|
||||
LogisticsData[] private _logisticsData; //全部物流信息数组
|
||||
|
||||
uint public recordCount;//所有记录条数
|
||||
uint public queryCount;//所有查询次数
|
||||
|
||||
//可自行添加形参和返回值
|
||||
function addLogistics(address _cargo,address _orgin,【请补充】) public {
|
||||
//TODO:”请补充缺失代码
|
||||
}
|
||||
|
||||
//可自行添加形参和返回值
|
||||
function getLogisticsInfo(address _cargo) public returns(LogisticsData[] memory _cargoLogisticsData) {
|
||||
|
||||
//TODO:”请补充缺失代码
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
@tab Material.sol
|
||||
```solidity
|
||||
pragma solidity 0.6.10;
|
||||
|
||||
contract Material {
|
||||
struct Material {
|
||||
address owner;
|
||||
string name;
|
||||
string id;
|
||||
string memo;
|
||||
uint createdAt;
|
||||
bool exist;
|
||||
}
|
||||
|
||||
mapping(string => Material) public materials;
|
||||
//可自行添加形参和返回值
|
||||
function newMaterial(string memory _name,string memory _id,【请补充】) public {
|
||||
|
||||
//TODO:”请补充缺失代码
|
||||
}
|
||||
|
||||
|
||||
//可自行添加形参和返回值
|
||||
function getMaterial(string memory id) public view returns(【请补充】) {
|
||||
|
||||
//TODO:”请补充缺失代码
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
:::
|
||||
|
||||
## 题目五
|
||||
|
||||
题目:
|
||||
|
||||
1.个人签章信息接口编码
|
||||
|
||||
(1)编写个人签章智能合约的实体接口,完成实体通用数据的初始化,实现签章和用户实体信息上链的功能
|
||||
|
||||
(2)编写签章信息上链的接口,实现Seal合约的构造函数;
|
||||
|
||||
(3)基于给定的智能合约代码以及注释,完成ElectronicSeal合约判断多人签章文件编号是否存在的函数。
|
||||
|
||||
2.电子印章接口编码
|
||||
|
||||
(1)基于给定的智能合约代码以及注释,完成ElectronicSeal合约获取多人签章信息函数;
|
||||
|
||||
(2)基于给定的智能合约代码以及注释,完成ElectronicSeal合约多人签章函数。
|
||||
|
||||
::: code-tabs
|
||||
@tab xxx.sol
|
||||
```solidity
|
||||
```
|
||||
:::
|
||||
## 题目六
|
||||
|
||||
题目:
|
||||
|
||||
1.食品信息(Food)、成员信息(Member)、生产订单信息(Productions)的结构体功能编码(6分)
|
||||
|
||||
(1)编写食品信息实体功能。(2分)
|
||||
|
||||
(2)完善智能合约中用户结构体内容(2分)
|
||||
|
||||
(3)编写生产订单(Productions)结构体信息。(2分)
|
||||
|
||||
2.食品溯源(Trace)的接口编码(6分)
|
||||
|
||||
(1)根据食品信息结构体,完成食品信息添加相应功能(2分)
|
||||
|
||||
(2)编写食品溯源收购商创建收购订单功能。
|
||||
|
||||
(3)编写食品溯源创建运输订单功能。
|
||||
|
||||
3.角色(Role)管理的接口编码(4分)
|
||||
|
||||
(1)编写食品溯源增加角色接口,实现添加角色的功能。(1分)
|
||||
|
||||
(2)编写食品溯源获取角色功能。(1分)
|
||||
|
||||
(3)编写食品溯源修改角色功能。(2分)
|
||||
|
||||
::: code-tabs
|
||||
@tab xxx.sol
|
||||
```
|
||||
```
|
||||
:::
|
||||
## 题目七
|
||||
|
||||
原题:
|
||||
**子任务2-2-1:航班延误保险购买合约编码(5分)**
|
||||
|
||||
(1)编写航班保险购买上链接口,完成只有购买机票的用户可以购买保险、必须缴纳正确的保费金额、购买保险的时间不能超过购买机票后的0.5小时的功能,符合条件则用户可以购买保险,将用户购买保险状态上链;(3分)
|
||||
|
||||
(2)编写退保接口,完成保险公司预存赔偿金后,用户无法退保,反之用户可退保的功能,将用户退保状态上链,并退还用户保费功能。(2分)
|
||||
|
||||
**子任务2-2-2:航班信息存证上链合约编码**
|
||||
|
||||
(1)编写获取航班信息接口,实现依据航班号获得航班号对应的计划起飞时间、实际起飞时间、到达时间、是否延误状态的功能;
|
||||
|
||||
(2)编写判断航班是否延误接口,实现依据航班号获得航班号对应的航班是否延误,得到航班是否延误的结果功能。
|
||||
|
||||
**子任务2-2-3:航班延误险理赔合约编码**
|
||||
|
||||
(1)编写客户理赔接口,实现如果航班延误超过4小时,将赔偿金赔偿给乘客的功能;
|
||||
|
||||
(2)编写保险公司收取保费接口,实现如果航班没有延误或者延误时间少于4小时,将保费转账给保险公司,并退还赔偿金的功能。
|
||||
|
||||
::: code-tabs
|
||||
@tab Claims.sol
|
||||
```solidity
|
||||
pragma solidity ^0.6.10;
|
||||
|
||||
import "./FlightDelayInsurance.sol";
|
||||
|
||||
contract Claims is FlightDelayInsurance{
|
||||
|
||||
// 定义航班信息结构体
|
||||
struct Flight {
|
||||
uint256 departureTime; // 航班出发时间
|
||||
uint256 delayTime; // 航班延误时间
|
||||
bool isDelayed; // 航班是否延误
|
||||
bool isInsured; // 航班是否购买保险
|
||||
}
|
||||
|
||||
// 定义保险公司地址
|
||||
address public insuranceCompany;
|
||||
|
||||
// 定义航班信息映射
|
||||
mapping(bytes32 => Flight) public flights;
|
||||
|
||||
// 定义购买保险事件
|
||||
event BuyInsurance(bytes32 flightKey, address passenger, uint256 premium);
|
||||
|
||||
// 定义航班延误事件
|
||||
event FlightDelay(bytes32 flightKey, uint256 delayTime);
|
||||
|
||||
// 定义理赔事件
|
||||
event Claim(bytes32 flightKey, address passenger, uint256 amount);
|
||||
|
||||
// 定义保险公司预存的赔偿金
|
||||
uint256 public compensationNew;
|
||||
|
||||
// 构造函数,初始化保险公司地址和赔偿金
|
||||
constructor() FlightDelayInsurance(platformS,airlineV,insuranceCompanyC,premium,compensation) public {
|
||||
insuranceCompany = msg.sender;
|
||||
compensationNew = compensation;
|
||||
}
|
||||
|
||||
// 更新航班信息函数
|
||||
function updateFlight(bytes32 flightKey, uint256 departureTime, uint256 delayTime, bool isDelayed) payable public {
|
||||
// 判断调用者是否为保险公司
|
||||
require(msg.sender == insuranceCompany, "Only insurance company can update flight information");
|
||||
// 更新航班信息
|
||||
flights[flightKey].departureTime = departureTime;
|
||||
flights[flightKey].isDelayed = isDelayed;
|
||||
|
||||
/*********** 客户理赔接口 **********/
|
||||
//任务2-2-3,请编写合约代码
|
||||
|
||||
|
||||
|
||||
/********** 客户理赔接口 ***********/
|
||||
|
||||
/*********** 保险公司收取保费接口开发 **********/
|
||||
//任务2-2-3,请编写合约代码
|
||||
|
||||
|
||||
|
||||
|
||||
/********** 保险公司收取保费接口开发 ***********/
|
||||
// 触发航班延误事件
|
||||
emit FlightDelay(flightKey, delayTime);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
@tab FlightDelayInsurance.sol
|
||||
```solidity
|
||||
pragma solidity ^0.6.10;
|
||||
|
||||
contract FlightDelayInsurance {
|
||||
address public platformS; // 平台S的地址
|
||||
address public airlineV; // 航空公司V的地址
|
||||
address public insuranceCompanyC; // 保险公司C的地址
|
||||
uint public premium; // 保险费
|
||||
uint public compensation; // 赔偿金额
|
||||
uint public purchaseTime; // 购买保险的时间
|
||||
uint public depositTime; // 存入赔偿金额的时间
|
||||
bool public purchased; // 是否购买了保险
|
||||
bool public deposited; // 是否存入了赔偿金额
|
||||
mapping(address => bool) public insured; // 已退保的用户
|
||||
mapping(address => bool) public policy; // 已生成保单的用户
|
||||
mapping(address => bool) public purchasedTicket; // 已购买机票的用户
|
||||
|
||||
constructor(address _platformS, address _airlineV, address _insuranceCompanyC, uint _premium, uint _compensation) public {
|
||||
platformS = _platformS; // 初始化平台S的地址
|
||||
airlineV = _airlineV; // 初始化航空公司V的地址
|
||||
insuranceCompanyC = _insuranceCompanyC; // 初始化保险公司C的地址
|
||||
premium = _premium; // 初始化保险费
|
||||
compensation = _compensation; // 初始化赔偿金额
|
||||
}
|
||||
|
||||
function purchaseTicket() public {
|
||||
require(!purchasedTicket[msg.sender], "Ticket has already been purchased"); // 该用户已购买机票
|
||||
purchasedTicket[msg.sender] = true; // 标记该用户已购买机票
|
||||
purchaseTime = block.timestamp;
|
||||
}
|
||||
/*********** 航班保险购买上链接口开发 **********/
|
||||
//任务2-2-1,请编写合约代码
|
||||
|
||||
|
||||
|
||||
|
||||
/********** 航班保险购买上链接口开发 ***********/
|
||||
function depositCompensation() public payable {
|
||||
require(msg.sender == insuranceCompanyC, "Only insurance company C can deposit compensation"); // 只有保险公司C可以存入赔偿金额
|
||||
require(msg.value == compensation, "Compensation amount is incorrect"); // 赔偿金额不正确
|
||||
require(block.timestamp < depositTime + 2 hours, "Deposit time has expired"); // 存入赔偿金额的时间已过期
|
||||
deposited = true; // 标记已存入赔偿金额
|
||||
}
|
||||
|
||||
/*********** 退保接口开发 **********/
|
||||
//任务2-2-1,请编写合约代码
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/********** 退保接口开发 ***********/
|
||||
|
||||
function generatePolicy() public {
|
||||
require(deposited, "Compensation has not been deposited"); // 赔偿金额未存入,无法生成保单
|
||||
require(msg.sender == platformS, "Only platform S can generate policy"); // 只有平台S可以生成保单
|
||||
require(!policy[msg.sender], "Policy has already been generated"); // 该用户已生成保单
|
||||
policy[msg.sender] = true; // 标记该用户已生成保单
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
@tab FlightManagement.sol
|
||||
```solidity
|
||||
pragma solidity ^0.6.10;
|
||||
|
||||
contract FlightManagement {
|
||||
|
||||
// 航班结构体
|
||||
struct Flight {
|
||||
string flightNumber; // 航班号
|
||||
uint256 scheduledDepartureTime; // 计划起飞时间
|
||||
uint256 actualDepartureTime; // 实际起飞时间
|
||||
uint256 arrivalTime; // 到达时间
|
||||
bool delayed; // 是否延误
|
||||
}
|
||||
|
||||
// 航班号到航班信息的映射
|
||||
mapping(string => Flight) flights;
|
||||
|
||||
// 添加航班
|
||||
function addFlight(string memory _flightNumber, uint256 _scheduledDepartureTime, uint256 _arrivalTime) public {
|
||||
flights[_flightNumber] = Flight(_flightNumber, _scheduledDepartureTime, 0, _arrivalTime, false);
|
||||
}
|
||||
|
||||
// 更新实际起飞时间
|
||||
function updateActualDepartureTime(string memory _flightNumber, uint256 _actualDepartureTime) public {
|
||||
flights[_flightNumber].actualDepartureTime = _actualDepartureTime;
|
||||
}
|
||||
|
||||
/*********** 获取航班信息接口开发 **********/
|
||||
//任务2-2-2,请编写合约代码
|
||||
|
||||
|
||||
|
||||
|
||||
/********** 获取航班信息接口开发 ***********/
|
||||
|
||||
/*********** 编写判断航班是否延误接口开发 **********/
|
||||
//任务2-2-2,请编写合约代码
|
||||
|
||||
|
||||
|
||||
|
||||
/********** 编写判断航班是否延误接口开发 ***********/
|
||||
}
|
||||
|
||||
```
|
||||
:::
|
||||
## 题目八
|
||||
|
||||
题目:
|
||||
子任务2-2-1:信息管理合约编码
|
||||
1. 编写检索个人信息接口,完成患者通过身份证号检索其姓名、性别、年龄的功能;
|
||||
2. 编写信息管理接口,完成允许患者与医院和科室进行信息管理,通过身份证号检索到患者的个人信息,将预约信息显示给患者,并发送到患者的账户地址中的功能;
|
||||
子任务2-2-2:病历管理合约编码
|
||||
1. 编写新建病历接口,实现检索病人对应科室既往病历,授权医生查看,如果没有既往病历则创建一个新的病历功能;
|
||||
2. 编写结束就诊接口,实现检查病历是否已经填写,并结束病历咨询的功能。
|
||||
子任务2-2-3:病历查看合约编码
|
||||
根据需求用例文档在待补充源码中完成病历查看合约的编码,解决代码错误和警告,正确编译合约,功能调试正确,运行合约中的检查退款请求状态、自动批准退款请求接口功能。
|
||||
1.编写查看病人个人信息接口,实现获取指定病人个人信息功能;
|
||||
2.编写查看病人病情描述接口,实现获取指定病人病情描述功能。
|
||||
::: code-tabs
|
||||
@tab xxx.sol
|
||||
```
|
||||
```
|
||||
:::
|
||||
## 题目九
|
||||
|
||||
题目:
|
||||
|
||||
子任务2-2-1:合同管理合约编码
|
||||
|
||||
(1)编写房东签署合同接口,完成本合同位置只允许房东签署,通过合同中的信息生成租赁合同的链上哈希,触发协议签署合同的功能,其中合同中的信息包括房东链上账户、租客链上账户、租赁开始时间、租赁结束时间、月租金额、押金金额、交租时间;
|
||||
|
||||
(2)编写租金支付接口,完成只允许租客支付租金的规则,检查支付的租金金额是否正确,触发记录租金支付情况的功能。
|
||||
|
||||
子任务2-2-2:违约管理合约编码
|
||||
|
||||
(1)编写房东终止合同接口,实现房东终止合同判断,如果租客已经终止合同则合同无效,如果合同有效,对合同终止状态进行标记,将剩余押金退还给租客的功能;
|
||||
|
||||
(2)编写租客终止合同接口,实现租客终止合同判断,如果房东已经终止合同则合同无效,如果合同有效,对合同终止状态进行标记,将剩余押金退还给房东的功能。
|
||||
|
||||
子任务2-2-3:押金管理合约编码
|
||||
|
||||
根据需求用例文档在待补充源码中完成押金管理合约的编码,解决代码错误和警告,正确编译合约,功能调试正确,运行合约中的租客缴纳押金情况查询、房东收取押金情况查询接口功能。
|
||||
|
||||
(1)编写租客缴纳押金情况查询接口,实现查询租客是狗已缴纳押金功能;
|
||||
|
||||
(2)编写房东收取押金情况查询接口,实现房东是否已收到押金的功能。
|
||||
|
||||
::: code-tabs
|
||||
@tab xxx.sol
|
||||
```
|
||||
```
|
||||
:::
|
||||
## 题目十
|
||||
|
||||
题目:
|
||||
|
||||
子任务2-2-1:账户管理合约编码
|
||||
|
||||
根据需求用例文档在待补充源码中完成账户管理合约的编码,解决代码错误和警告,正确编译合约,功能调试正确。需要编写生成账户接口,完成从外部部门检索姓名、年龄、雇主、开始日期、工资、缴费基数,将人员信息进行综合存储功能。
|
||||
|
||||
子任务2-2-2:费用管理合约编码
|
||||
|
||||
根据需求用例文档在待补充源码中完成费用管理合约的编码,解决代码错误和警告,正确编译合约,功能调试正确,运行添加新职工账户、添加新雇主账户。
|
||||
|
||||
1.编写添加新职工账户接口,实现当账户不存在,只有管理员可以添加职工账户,职工新账户中账户余额为0,未被赞助的功能;
|
||||
|
||||
2.编写添加新雇主账户接口,实现当账户不存在,只有管理员可以添加雇主账户,雇主新账户中账户余额为0,已被赞助的功能;
|
||||
|
||||
子任务2-2-3:保险转移合约编码
|
||||
|
||||
根据需求用例文档在待补充源码中完成保险转移合约的编码,解决代码错误和警告,正确编译合约,功能调试正确,运行合约中的申请转移关系、接收账户转移接口。
|
||||
|
||||
1.编写申请转移关系接口,实现创建申请、添加到申请列表功能,其中创建申请需要设置申请人地址、原城市、目标城市、停缴状态、批准状态;
|
||||
|
||||
2.编写接收账户转移接口,实现获取账户,进行账户授权状态、接收状态、个人账户基金、统筹账户基金、养老保险账户的信息设置;
|
||||
|
||||
::: code-tabs
|
||||
@tab xxx.sol
|
||||
```
|
||||
```
|
||||
:::
|
||||
56
docs/notes/programming/solidity/other/hardhat.md
Normal file
56
docs/notes/programming/solidity/other/hardhat.md
Normal file
@@ -0,0 +1,56 @@
|
||||
---
|
||||
title: Hardhat 相关知识
|
||||
createTime: 2025/10/14 14:57:06
|
||||
permalink: /programming/solidity/other/hardhat/
|
||||
---
|
||||
|
||||
## HardHat2 部署
|
||||
### Node.js v16 安装
|
||||
HardHat2 需要 Node.js v16 及以上的版本
|
||||
这里给出的是通过 npm 来将旧版本升级到 Node.js v16 的,如果想要直接安装的话通过 `yum install nodejs` 或者 `apt install nodejs` 来即可。
|
||||
```bash
|
||||
npm config set registry https://registry.npmmirror.com
|
||||
sudo npm install -g n
|
||||
sudo n 16
|
||||
```
|
||||
安装完成之后可以查看版本,如果没有更新可以重新刷新一下环境(直接退出重连最简单)
|
||||
```bash
|
||||
node -v
|
||||
# v16.20.2
|
||||
npm -v
|
||||
# 6.14.15
|
||||
```
|
||||
### 安装 Hardhat2
|
||||
创建一个 hardhat2-project 目录,初始化 npm 项目,注意这里不要用 hardhat 作为项目名
|
||||
```bash
|
||||
mkdir ~/hardhat2-project
|
||||
cd ~/hardhat2-project
|
||||
npm init -y
|
||||
```
|
||||
安装 Hardhat 2
|
||||
```bash
|
||||
npm install --save-dev hardhat@^2.23.0
|
||||
```
|
||||
初始化 Hardhat2 项目
|
||||
```bash
|
||||
npx hardhat init
|
||||
```
|
||||
选择
|
||||
```bash
|
||||
✔ What do you want to do? · Create a JavaScript project
|
||||
✔ Hardhat project root: · /root/hardhat2-project
|
||||
✔ Do you want to add a .gitignore? (Y/n) · y
|
||||
✔ Help us improve Hardhat with anonymous crash reports & basic usage data? (Y/n) · y
|
||||
✔ Do you want to install this sample project's dependencies with npm (@nomicfoundation/hardhat-toolbox)? (Y/n) · y
|
||||
```
|
||||
## 使用 Hardhat2 编译测试合约
|
||||
在 hardhat2-project 目录下,执行以下命令来编译合约
|
||||
合约都会放在 `contracts` 目录下,这里我们选择了一个默认的合约,因此可以直接编译
|
||||
```bash
|
||||
# 测试编译
|
||||
npx hardhat compile
|
||||
# 运行测试
|
||||
npx hardhat test
|
||||
# 查看可用任务
|
||||
npx hardhat help
|
||||
```
|
||||
88
docs/notes/programming/solidity/other/miscellaneous.md
Normal file
88
docs/notes/programming/solidity/other/miscellaneous.md
Normal file
@@ -0,0 +1,88 @@
|
||||
---
|
||||
title: 一些没分类的小知识
|
||||
createTime: 2025/10/12 15:34:38
|
||||
permalink: /programming/solidity/other/miscellaneous/
|
||||
---
|
||||
|
||||
## 关于 memory 和 storage 存储类型
|
||||
|
||||
- `storage`:合约的持久化状态数据,保存在链上状态。对 `storage` 的写入最昂贵,读取也比内存贵;修改会永久生效。
|
||||
- `memory`:函数调用期间的临时数据,函数返回后即释放。对 `memory` 的更改不会持久化。
|
||||
- (补充)`calldata`:外部函数参数的只读数据位置,零拷贝、不可修改,用于节省 gas。
|
||||
|
||||
### 生命周期与成本
|
||||
- `storage` 写入昂贵、读取较贵;适合保存需要长期存在的状态。
|
||||
- `memory` 在函数结束时释放,读取/写入相对便宜;适合临时计算与返回值。
|
||||
- 复杂引用类型(数组、`struct`、`mapping`、`string`、`bytes`)在函数参数或局部变量处通常必须显式标注数据位置。
|
||||
|
||||
### 默认与必须声明
|
||||
- 状态变量总是位于 `storage`(例如 `User[] public users;`)。
|
||||
- 外部函数(`external`)的复杂类型参数默认是 `calldata`;内部/公共函数需要显式标注 `memory` 或 `storage`。
|
||||
- 局部变量的复杂类型必须指定数据位置,否则编译报错。
|
||||
|
||||
### 拷贝与引用语义
|
||||
- 从 `storage` 读取到 `memory` 会“复制”数据;修改 `memory` 副本不影响原始 `storage`。
|
||||
- 使用 `storage` 局部变量可以得到对状态数据的“引用”,对其赋值会持久化。
|
||||
|
||||
```solidity
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
contract Users {
|
||||
struct User { string name; uint age; }
|
||||
User[] public users;
|
||||
|
||||
function add(string memory name, uint age) external {
|
||||
users.push(User(name, age)); // 写入 storage
|
||||
}
|
||||
|
||||
function updateName(uint i, string memory newName) external {
|
||||
User storage u = users[i]; // storage 引用(指向链上状态)
|
||||
u.name = newName; // 修改持久化生效
|
||||
}
|
||||
|
||||
function tryUpdate(uint i) external {
|
||||
User memory u = users[i]; // 从 storage 复制到 memory
|
||||
u.age = 99; // 仅修改副本,不会影响链上状态
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 在内部函数传递 storage 引用
|
||||
- 仅内部/私有函数可以接收 `storage` 引用参数,从而直接修改状态;外部函数参数不能是 `storage`。
|
||||
|
||||
```solidity
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
contract Users2 {
|
||||
struct User { string name; uint age; }
|
||||
User[] public users;
|
||||
|
||||
function _inc(User storage u) internal { u.age += 1; }
|
||||
|
||||
function birthday(uint i) external {
|
||||
_inc(users[i]); // 传递 storage 引用,持久化修改
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 动态 memory 数组与限制
|
||||
- 可在 `memory` 中构造动态数组:`new uint[](n)`;适合作为返回值或临时计算。
|
||||
- `mapping` 只能存在于 `storage`,不能在 `memory` 中创建或拷贝。
|
||||
|
||||
```solidity
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
contract Arrays {
|
||||
function make(uint n) external pure returns (uint[] memory a) {
|
||||
a = new uint[](n);
|
||||
for (uint i = 0; i < n; i++) a[i] = i;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 常见坑与实践建议
|
||||
- 给 `storage` 变量整体赋值会进行深拷贝或引用变更(依据类型),要明确拷贝成本与语义。
|
||||
- 修改 `memory` 副本不会持久化;要修改链上状态请使用 `storage` 引用。
|
||||
- 大型 `string/bytes/数组` 在 `memory↔storage` 间复制成本高,尽量减少不必要的复制。
|
||||
- 外部函数能用 `calldata` 的地方尽量使用(只读参数),节省 gas。
|
||||
|
||||
Reference in New Issue
Block a user