Allureluoli 二月FEBRUARY

区块链国赛笔记 08|供应链金融合约:企业、票据与转账业务

这组笔记围绕企业、银行和票据流转展开。两个较长的合约是分别学习的实现,不是可以直接拼接的同一份源码。

本文由个人备赛旧笔记整理,整理日期为 2026-11-03。命令、输出和测试记录属于当时的实验环境;本文未重新搭建整套环境验证。

阅读前先确认

先画清谁能创建票据、谁持有余额、转让后如何追溯,再逐条核对权限和金额守恒。旧版 Solidity 示例需要配套编译器和依赖。

合约开发1

合约开发


比赛要求如下

使用Solidity编程语言开 发智能合约,包括存储合约设计开发,接口合约设计开发,最终实现区块链存证功能

  • 存储合约设计开发
  • 接口合约设计开发
  • 区块链存证功能

省赛选拔题解剖

此题是债卷凭证题

原始资产上链,进行审核校验

确认贸易关系和身份

保证资产真实性

债券基于供应链层层拆分和流转

追溯到最底层资产

实现信用穿透


四个对象

主要对象 :

  • 供应商
  • 需求企业

bankToCompanyReceipt:银行向公司交易;

companyToCompanyReceipt:公司与公司交易;

companyToBankReceipt:公司与银行交易。

实现三个接口


合约开发2 金融合约

金融合约开发

开发流程:

合约部署的所有者 添加银行和公司

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
pragma solidity ^0.4.25;

import './Ownable.sol';

// SupplyChainFin is Ownable 开局先继承
contract SupplyChainFin is Ownable {
// 利用 struct 结构体制定表示企业的信息结构
struct Company {
string companyName;
address companyAddress;
uint creditAsset;
uint[] acceptReceiptIndex;
uint[] sendReceiptIndex;
}

// 利用 struct 结构体制定表示票据的信息结构
struct Receipt {
address senderAddress;
address accepterAddress;
uint8 receiptType;
uint8 transferType;
uint amount;
bool isActive;
}

// 利用 mapping 快速通过地址得到企业信息
mapping(address => Company) public companies;

// 利用 mapping 快速通过ID得到票据信息
mapping(uint => Receipt) public receipts;

// 使用 address[] 专门存储不同的企业地址
address[] public companyAddresses;

// 状态变量
uint public receiptCount;

// 事件定义
event CompanyAdded(address indexed companyAddress, string companyName, uint creditAsset);
event ReceiptCreated(uint indexed receiptId, address sender, address accepter, uint amount);
event ReceiptTransferred(uint indexed receiptId, address oldAccepter, address newAccepter);

// 权限设计 onlyOwner - 实际上隐藏在 Ownable.sol
/**
* @dev Throws if called by any account other than the owner.
*/
// modifier onlyOwner() {
// require(msg.sender == owner);
// _;
// }

// 添加企业信息 - onlyOwner权限
function addCompany(address _companyAddress, string _companyName, uint _creditAsset) public onlyOwner returns (bool) {
// 通过 keccak256 对比哈希值,确认企业不存在
require(companies[_companyAddress].companyAddress == address(0), "Company already exists");

companies[_companyAddress] = Company({
companyName: _companyName,
companyAddress: _companyAddress,
creditAsset: _creditAsset,
acceptReceiptIndex: new uint,
sendReceiptIndex: new uint
});

// 添加到地址列表
companyAddresses.push(_companyAddress);

emit CompanyAdded(_companyAddress, _companyName, _creditAsset);
return true;
}

// 创建票据 - 保证交易的合法性
function createReceipt(address _accepterAddress, uint8 _receiptType, uint8 _transferType, uint _amount) public returns (uint) {
// 验证发送方企业存在
require(companies[msg.sender].companyAddress != address(0), "Sender company not registered");

// 验证接收方企业存在
require(companies[_accepterAddress].companyAddress != address(0), "Accepter company not registered");

// 验证金额有效性
require(_amount > 0, "Amount must be greater than 0");

// 创建票据
receiptCount++;
receipts[receiptCount] = Receipt({
senderAddress: msg.sender,
accepterAddress: _accepterAddress,
receiptType: _receiptType,
transferType: _transferType,
amount: _amount,
isActive: true
});

// 通过Receipt结构进行存证,存储交易记录
companies[msg.sender].sendReceiptIndex.push(receiptCount);
companies[_accepterAddress].acceptReceiptIndex.push(receiptCount);

emit ReceiptCreated(receiptCount, msg.sender, _accepterAddress, _amount);
return receiptCount;
}

// 转让票据 - 保证交易的合法性
function transferReceipt(uint _receiptId, address _newAccepter) public returns (bool) {
// 验证票据有效性
require(receipts[_receiptId].isActive, "Receipt is not active");

// 必须只能接受人创建此交易,也就是接受人承认这笔交易存在
require(receipts[_receiptId].accepterAddress == msg.sender, "Only current accepter can transfer");

// 验证新接收方企业存在
require(companies[_newAccepter].companyAddress != address(0), "New accepter company not registered");

address oldAccepter = receipts[_receiptId].accepterAddress;
receipts[_receiptId].accepterAddress = _newAccepter;

// 更新票据索引
_removeFromArray(companies[oldAccepter].acceptReceiptIndex, _receiptId);
companies[_newAccepter].acceptReceiptIndex.push(_receiptId);

emit ReceiptTransferred(_receiptId, oldAccepter, _newAccepter);
return true;
}

// 内部函数:从数组中移除元素
function _removeFromArray(uint[] storage array, uint value) internal {
for (uint i = 0; i < array.length; i++) {
if (array[i] == value) {
array[i] = array[array.length - 1];
array.length--;
break;
}
}
}

// 查询函数
function getCompany(address _companyAddress) public view returns (
string, address, uint, uint[], uint[]
) {
Company memory company = companies[_companyAddress];
return (
company.companyName,
company.companyAddress,
company.creditAsset,
company.acceptReceiptIndex,
company.sendReceiptIndex
);
}

function getReceipt(uint _receiptId) public view returns (
address, address, uint8, uint8, uint, bool
) {
Receipt memory receipt = receipts[_receiptId];
return (
receipt.senderAddress,
receipt.accepterAddress,
receipt.receiptType,
receipt.transferType,
receipt.amount,
receipt.isActive
);
}

// 获取所有企业地址
function getAllCompanies() public view returns (address[]) {
return companyAddresses;
}
}

  1. 继承结构:SupplyChainFin is Ownable 继承权限控制基础合约
  2. 结构体定义:Company 和 Receipt 结构体分别管理企业和票据数据
  3. 映射存储:mapping(address => Company) 和 mapping(uint => Receipt) 实现快速数据访问
  4. 地址列表:address[] companyAddresses 存储所有注册企业地址
  5. 权限控制:使用 onlyOwner 修饰符保护企业注册功能
  6. 哈希验证:通过地址比较验证企业存在性(替代哈希验证)
  7. 交易合法性:严格的发送方、接收方、金额验证
  8. 存证系统:Receipt 结构记录交易凭证,并更新企业票据索引
  9. 事件日志:完整的事件系统记录所有关键操作
  10. 工具函数:内部函数处理数组操作,保证数据一致性

转账合约设计

转账合约设计

示例:

供应链金融智能合约

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
pragma solidity ^0.4.22;
import './Ownable.sol';

//供应链金融智能合约
contract SupplyChainFin is Ownable {

//监管者信息结构体
struct Supervisor {
string supervisorName;
address supervisorAddress;
}

//公司信息结构体
struct Company {
string companyName;
address companyAddress;
uint creditAsset;
uint[] acceptReceiptIndex;
uint[] sendReceiptIndex;
}

//银行信息结构体
struct Bank {
string bankName;
address bankAddress;
uint creditAsset;
uint[] acceptReceiptIndex;
uint[] sendReceiptIndex;

}

//数字发票收据信息
struct Receipt {
address senderAddress;
address accepterAddress;
uint8 receiptType;
uint8 transferType;
uint amount;
}

//公司的Map,用于快速搜索
mapping(address => Company) companyMap;

//银行的Map,用于快速搜索
mapping(address => Bank) bankMap;

//发票的Map,用于快速搜索
mapping(uint => Receipt) receiptMap;

//监管者实体
Supervisor public superviosrIns;

//公司地址的数组
address[] public companies;

//银行地址的数组
address[] public banks;

//数字发票的索引
uint public receiptIndex;

//构造函数
constructor(string name) {
receiptIndex = 0;
superviosrIns = Supervisor(name, msg.sender);
}


function addCompany(string name, address companyAddress) returns(bool) {
Company memory newCompany = Company(name, companyAddress, 0, new uint, new uint);
companies.push(companyAddress);
companyMap[companyAddress] = newCompany;
return true;
}

function getCompany(address companyAddress) public view returns(string, address, uint, uint[], uint[]) {
Company company = companyMap[companyAddress];
return (company.companyName, company.companyAddress, company.creditAsset, company.acceptReceiptIndex, company.sendReceiptIndex);
}

function addBank(string name, address bankAddress, uint credit) public onlyOwner {
Bank memory newBank = Bank(name, bankAddress, credit, new uint, new uint);
banks.push(bankAddress);
bankMap[bankAddress] = newBank;
}

function getBank(address bankAddress) public view returns(string, address, uint, uint[], uint[]){
Bank bank = bankMap[bankAddress];
return (bank.bankName, bank.bankAddress, bank.creditAsset, bank.acceptReceiptIndex, bank.sendReceiptIndex);
}

function getAllCompanyAddress() public view returns(address[]) {
return companies;
}

function getAllBankAddress() public view returns(address[]) {
return banks;
}

function getReceipt(uint index) public view returns(address, address, uint8, uint8, uint) {
Receipt receipt = receiptMap[index];
return (receipt.senderAddress, receipt.accepterAddress, receipt.receiptType, receipt.transferType, receipt.amount);
}

//存证交易
// receiptType: 发票类型(存证、现金)
//1: 交易类型为存证
//2:交易类型为现金
// transferType: 交易类型
//1: 银行转账给公司
//2: 公司与公司间转账
//3: 公司转账给银行

//银行向公司交易(公司向银行提供交易存证)
function bankToCompanyReceipt(address senderAddress, address accepterAddress, uint amount, uint8 receiptType) returns(uint) {

//transferType :1 银行转账给公司
//必须只能接受人创建此交易,也就是接受人承认这笔交易存在
require(msg.sender == accepterAddress);
Bank memory bank = bankMap[accepterAddress];
Company memory company = companyMap[senderAddress];

//确认银行存在
if (keccak256(bank.bankName) == keccak256("")) {
return 404001;
}

//确认公司存在
if (keccak256(company.companyName) == keccak256("")) {
return 404002;
}

if (bank.creditAsset < amount) {
return 500001;
}

Receipt memory newReceipt = Receipt(senderAddress, accepterAddress, receiptType, 1, amount);
receiptIndex += 1;
receiptMap[receiptIndex] = newReceipt;
companyMap[senderAddress].sendReceiptIndex.push(receiptIndex);
bankMap[accepterAddress].acceptReceiptIndex.push(receiptIndex);
bankMap[accepterAddress].creditAsset -= amount;
companyMap[senderAddress].creditAsset += amount;
return 200;

}


//公司与公司交易
//接收存证的公司需要给发送存证的公司转账存证对应的数额
function companyToCompanyReceipt(address senderAddress, address accepterAddress, uint amount, uint8 receiptType) returns(uint) {

//transferType :2 公司与公司间转账
//存证接收的公司可以产生这一笔交易
require(msg.sender == accepterAddress);

Company memory senderCompany = companyMap[senderAddress];
Company memory accepterCompany = companyMap[accepterAddress];
//确认发送公司存在
if (keccak256(senderCompany.companyName) == keccak256("")) {
return 404001;
}

//确认接收公司存在
if (keccak256(accepterCompany.companyName) == keccak256("")) {
return 404002;
}

//如果存证接收的公司资产小于存证数额,那么就不能交易发送存证
if (accepterCompany.creditAsset < amount) {
return 500001;
}

//创建存证
Receipt memory newReceipt = Receipt(senderAddress, accepterAddress, receiptType, 2, amount);
receiptIndex += 1;
//记录存证(存证Map,公司Map对应地址的发送和接收存证列表)
receiptMap[receiptIndex] = newReceipt;
companyMap[senderAddress].sendReceiptIndex.push(receiptIndex);
companyMap[accepterAddress].acceptReceiptIndex.push(receiptIndex);

companyMap[senderAddress].creditAsset += amount;
companyMap[accepterAddress].creditAsset -= amount;
return 200;
}


//公司与银行交易
function companyToBankReceipt(address senderAddress, address accepterAddress, uint amount, uint8 receiptType) returns(uint) {

//transferType :3 公司向银行转账
//存证接收的公司可以产生这一笔交易
require(msg.sender == accepterAddress);

Bank memory bank = bankMap[senderAddress];
Company memory accepterCompany = companyMap[accepterAddress];

//确认发送公司存在
if (keccak256(bank.bankName) == keccak256("")) {
return 404001;
}

//确认接收公司存在
if (keccak256(accepterCompany.companyName) == keccak256("")) {
return 404002;
}

//如果存证接收的公司资产小于存证数额,那么就不能交易发送存证
if (accepterCompany.creditAsset < amount) {
return 500001;
}

//创建存证
Receipt memory newReceipt = Receipt(senderAddress, accepterAddress, receiptType, 3, amount);
receiptIndex += 1;
//记录存证(存证Map,公司Map对应地址的发送和接收存证列表)
receiptMap[receiptIndex] = newReceipt;
bankMap[senderAddress].sendReceiptIndex.push(receiptIndex);
companyMap[accepterAddress].acceptReceiptIndex.push(receiptIndex);
bankMap[senderAddress].creditAsset += amount;
companyMap[accepterAddress].creditAsset -= amount;
return 200;

}

}

设计解构:

  • SupplyChainFin is Ownable 开局先继承

  • 利用 struct 结构体制定表示 银行 公司 收据 的 三种信息结构

  • 利用mapping 快速通过地址得到信息

  • 使用address[] 专门存储不同的地址

  • 权限设计 onlyOwner

    ​ 实际上隐藏在 Ownable.sol

    •   /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
          require(msg.sender == owner);
          _;
        }
      
      1
      2
      3
      4
      5
      6
      7

      - 通过 `keccak256` 对比哈希值,确认部门存在

      ```solidity
      if (keccak256(company.companyName) == keccak256("")) {
      return 404002;
      }
  • 保证交易的合法性

    1
    2
    3
    4
    5
    //transferType :1 银行转账给公司
    //必须只能接受人创建此交易,也就是接受人承认这笔交易存在
    require(msg.sender == accepterAddress);
    Bank memory bank = bankMap[accepterAddress];
    Company memory company = companyMap[senderAddress];
  • 通过Receipt结构进行存证,存储交易记录

    1
    2
    3
    4
    5
    6
    7
    //创建存证
    Receipt memory newReceipt = Receipt(senderAddress, accepterAddress, receiptType, 2, amount);
    receiptIndex += 1;
    //记录存证(存证Map,公司Map对应地址的发送和接收存证列表)
    receiptMap[receiptIndex] = newReceipt;
    companyMap[senderAddress].sendReceiptIndex.push(receiptIndex);
    companyMap[accepterAddress].acceptReceiptIndex.push(receiptIndex);
  • 简单的交易计算

    1
    2
    bankMap[senderAddress].creditAsset += amount;
    companyMap[accepterAddress].creditAsset -= amount;
  • 返回 状态码 200 表示成功


## 复现时的自查

- 记录自己实际使用的版本、配置和缺失依赖。
- 对照本篇关键步骤,保存命令或调用结果,并解释结果代表什么。
- 对失败或与原记录不一致的情况保留错误信息,不把历史输出当成自己的验证结果。

[返回系列导读](/guosai-notes/) · [上一篇](/guosai-notes/solidity-abi/) · [下一篇](/guosai-notes/foundry/)

评论COMMENTS