2025-10-20-SimpleContract&test

2025-10-20-SimpleContract&test

十月 20, 2025

智能合约开发与测试

第1题:按要求完成智能合约开发开发与测

【要求】 使用Solidity 语言进行智能合约开发,根据需求用例文档在待补充源码中完 成程序接口功能的编码,解决代码错误和警告,正确编译合约,功能调试正确, 运行合约进行业务功能的验证,成功获取合约的abi,将合约部署至区块链,获 取部署的合约信息,将任务中编写代码、运行截图、部署至区块链截图保存至指 定位置。

【任务】

1.完善bank合约开发,根据合约里面的代码注释,完善缺失的合约中缺失的代码。

2.基于hardhat完成合约的功能测试,针对bank合约中的函数,完成功能测试

3.针对bank合约中的withdraw函数,完成边界值测试

要求:

1.测试区块金额大于0的情况 2.测试账户余额大于取款金额的情况

3.测试账户存在的情况


作答

只能自己想一个合约了

第一个合约叫 Bank 那就是和银行有关的

账户:开户,销户

存款:存钱,取钱,查询余额,转账

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
// SPDX-License-Identifier: MIT
pragma solidity 0.4.2;

contract Bank2{

struct Account{
address owner;
uint balance;
uint time;
}

mapping (address => Account) public accounts; // 映射结构存储

// 创建账户的事件 提供监听
event AccountCreate(address account,uint time);

// 创建账户的函数

function createAccount() public{

address account = msg.sender;

accounts[account] = Account({
owner: account,
balance: 0,
time: now
});
AccountCreate(account,now);

}

function checkAccount(address account) public returns(bool){
return accounts[account].owner != address(0); // 检查是不是空地址罢了
}
// 存款
function deposit() public payable {

if (!checkAccount(msg.sender)) {
createAccount();
}
accounts[msg.sender].balance += msg.value;
accounts[msg.sender].time = now;

}

}

需要写一个 withdraw 大概是提现函数的意思吧

1
2
3
4
5
accounts[msg.sender].balance -= amount;

// 转账给用户
(bool success, ) = payable(msg.sender).call{value: amount}("");
require(success, "Withdrawal failed");

完善后的完整合约

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
// SPDX-License-Identifier: MIT
pragma solidity 0.4.20;

contract Bank {

struct Account {
address owner;
uint balance;
uint time;
}

mapping (address => Account) public accounts;

// 事件声明
event AccountCreate(address account, uint time);
event WithdrawSuccess(address account, uint amount, uint time);
event WithdrawFailed(address account, uint amount, string reason, uint time);

// 创建账户
function createAccount() public {
address account = msg.sender;

// 不允许重复创建账户
if (checkAccount(account)) {
revert();
}


accounts[account] = Account({
owner: account,
balance: 0,
time: now
});
AccountCreate(account, now);
}

// 检查账户是否存在
function checkAccount(address account) public constant returns(bool) {
return accounts[account].owner != address(0);
}

// 存款
function deposit() public payable {
if (!checkAccount(msg.sender)) {
createAccount();
}
accounts[msg.sender].balance += msg.value;
accounts[msg.sender].time = now;
}

// 检查余额
function checkBalance() public constant returns (uint) {
address account = msg.sender;
if (!checkAccount(account)) {
return 0;
}
return accounts[account].balance;
}

// 完善的取款函数
function withdraw(uint amount) public {
address account = msg.sender;


// 1. 检查账户是否存在
if (!checkAccount(account)) {
WithdrawFailed(account, amount, "用户不存在", now);
revert();
}

// 2. 检查余额是否充足
if (accounts[account].balance < amount) {
WithdrawFailed(account, amount, "余额不足", now);
revert();
}

// 3. 检查取款金额是否有效
if (amount == 0) {
WithdrawFailed(account, amount, "取款金额必须大于0", now);
revert();
}

// 4. 更新账户余额
accounts[account].balance -= amount;
accounts[account].time = now;

// 5. 转账给用户
if (!account.send(amount)) {
// 如果转账失败,恢复余额
accounts[account].balance += amount;
WithdrawFailed(account, amount, "转账失败", now);
revert();
}

// 6. 记录成功事件
WithdrawSuccess(account, amount, now);
}

function transfer(address to, uint amount) public {
address from = msg.sender;

// 1. 检查发送方账户
if (!checkAccount(from)) {
WithdrawFailed(from, amount, "发送方账户不存在", now);
revert();
}

// 2. 检查接收方账户
if (!checkAccount(to)) {
WithdrawFailed(from, amount, "接收方账户不存在", now);
revert();
}

// 3. 检查转账金额是否有效
if (amount == 0) {
WithdrawFailed(from, amount, "转账金额必须大于0", now);
revert();
}

// 4. 检查发送方余额是否充足
if (accounts[from].balance < amount) {
WithdrawFailed(from, amount, "发送方余额不足", now);
revert();
}

// 5. 更新余额
accounts[from].balance -= amount;
accounts[to].balance += amount; // 关键:增加接收方余额

// 3. 更新时间戳
accounts[from].time = now;
accounts[to].time = now;

// 4. 记录事件
TransferSuccess(from, to, amount, now);
}

// 转账成功事件
event TransferSuccess(address from, address to, uint amount, uint time);

// 获取账户信息
function getAccountInfo() public constant returns (address, uint, uint) {
address account = msg.sender;
if (!checkAccount(account)) {
return (address(0), 0, 0);
}
return (accounts[account].owner, accounts[account].balance, accounts[account].time);
}

}

大概要这么写,难说

边界值测试话 下界 上界 … 极端大值和极端小值

基本功能测试

测试代码

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
const { expect } = require("chai");
const { ethers } = require("hardhat");

describe("Bank 合约", function () {
let bank;
let owner, user1, user2, user3;

beforeEach(async function () {
[owner, user1, user2, user3] = await ethers.getSigners();
const Bank = await ethers.getContractFactory("Bank");
bank = await Bank.deploy();
});

describe("部署测试", function () {
it("成功部署合约", async function () {
expect(bank.address).to.not.equal("0x0000000000000000000000000000000000000000");
});
});

describe("账户管理", function () {
it("应该允许创建账户", async function () {
await bank.connect(user1).createAccount();
expect(await bank.checkAccount(user1.address)).to.be.true;
});

it("不允许重复创建账户", async function () {
await bank.connect(user1).createAccount();
await expect(bank.connect(user1).createAccount()).to.be.reverted;
});
});

describe("存款测试", function () {
it("应该允许用户存款", async function () {
await bank.connect(user1).createAccount();
await bank.connect(user1).deposit({ value: 100 });
expect(await bank.connect(user1).checkBalance()).to.equal(100);

});
});

describe("取款测试", function () {
it("应该允许用户取款", async function () {
await bank.connect(user1).createAccount();
await bank.connect(user1).deposit({ value: 100 });
await bank.connect(user1).withdraw(50);
expect(await bank.connect(user1).checkBalance()).to.equal(50);
});
});

describe("转账测试", function () {
it("应该允许用户转账", async function () {

await bank.connect(user2).createAccount();
await bank.connect(user1).createAccount();
await bank.connect(user1).deposit({ value: 100 });
await bank.connect(user1).transfer(user2.address, 50);
expect(await bank.connect(user1).checkBalance()).to.equal(50);
expect(await bank.connect(user2).checkBalance()).to.equal(50);
});
});
});

测试结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
PS E:\code\solidity_project\bank3> pnpm hardhat test test/Bank.test.js
Compiled 1 Solidity file successfully (evm target: unknown evm version for solc version 0.4.20).


Bank 合约
部署测试
✔ 成功部署合约 (46ms)
账户管理
✔ 应该允许创建账户
✔ 不允许重复创建账户
存款测试
✔ 应该允许用户存款
取款测试
✔ 应该允许用户取款
转账测试
✔ 应该允许用户转账


6 passing (941ms)

边界值测试js脚本编写

我只能说 Easy

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
describe("取款边界值测试", function () {
let user1, user2;

beforeEach(async function () {
[user1, user2] = await ethers.getSigners();
await bank.connect(user1).createAccount();
await bank.connect(user1).deposit({ value: 100 });
});

// 1. 测试取款金额大于0的情况
describe("测试取款金额大于0的情况", function () {
it("应该允许取款金额为1(最小值)", async function () {
await bank.connect(user1).withdraw(1);
expect(await bank.connect(user1).checkBalance()).to.equal(99);
});

it("应该允许取款金额为余额的一半", async function () {
await bank.connect(user1).withdraw(50);
expect(await bank.connect(user1).checkBalance()).to.equal(50);
});

it("应该允许取款金额为余额-1", async function () {
await bank.connect(user1).withdraw(99);
expect(await bank.connect(user1).checkBalance()).to.equal(1);
});

it("应该允许取款金额等于余额", async function () {
await bank.connect(user1).withdraw(100);
expect(await bank.connect(user1).checkBalance()).to.equal(0);
});

it("应该拒绝取款金额为0", async function () {
await expect(bank.connect(user1).withdraw(0))
.to.be.reverted;
});

it("应该拒绝取款金额为负数(Solidity会自动处理)", async function () {
// uint 类型不能为负数,Solidity 会自动拒绝
// 这个测试主要是验证类型安全
});
});

// 2. 测试账户余额大于取款金额的情况
describe("测试账户余额大于取款金额的情况", function () {
it("余额100,取款50,应该成功", async function () {
await bank.connect(user1).withdraw(50);
expect(await bank.connect(user1).checkBalance()).to.equal(50);
});

it("余额100,取款99,应该成功", async function () {
await bank.connect(user1).withdraw(99);
expect(await bank.connect(user1).checkBalance()).to.equal(1);
});

it("余额100,取款100,应该成功", async function () {
await bank.connect(user1).withdraw(100);
expect(await bank.connect(user1).checkBalance()).to.equal(0);
});

it("余额100,取款101,应该失败", async function () {
await expect(bank.connect(user1).withdraw(101))
.to.be.reverted;
});

it("余额100,取款最大值,应该失败", async function () {
await expect(bank.connect(user1).withdraw(115792089237316195423570985008687907853269984665640564039457584007913129639935n))
.to.be.reverted;
});

});

// 3. 测试账户存在的情况
describe("测试账户存在的情况", function () {
it("已创建账户的用户应该能取款", async function () {
await bank.connect(user1).withdraw(50);
expect(await bank.connect(user1).checkBalance()).to.equal(50);
});

it("未创建账户的用户应该不能取款", async function () {
await expect(bank.connect(user2).withdraw(50))
.to.be.reverted;
});

it("创建账户但余额为0的用户应该不能取款", async function () {
await bank.connect(user2).createAccount();
await expect(bank.connect(user2).withdraw(1))
.to.be.reverted;
});
});

// 4. 额外边界测试
describe("额外边界测试", function () {
it("连续多次取款", async function () {
await bank.connect(user1).withdraw(30);
expect(await bank.connect(user1).checkBalance()).to.equal(70);

await bank.connect(user1).withdraw(30);
expect(await bank.connect(user1).checkBalance()).to.equal(40);

await bank.connect(user1).withdraw(40);
expect(await bank.connect(user1).checkBalance()).to.equal(0);
});

it("取款后再次存款再取款", async function () {
await bank.connect(user1).withdraw(50);
expect(await bank.connect(user1).checkBalance()).to.equal(50);

await bank.connect(user1).deposit({ value: 50 });
expect(await bank.connect(user1).checkBalance()).to.equal(100);

await bank.connect(user1).withdraw(100);
expect(await bank.connect(user1).checkBalance()).to.equal(0);
});

it("测试大额取款", async function () {
// 存入大额资金
await bank.connect(user3).createAccount();

await bank.connect(user3).deposit({ value: 999997133560957503749n});

// 取款大额资金
await bank.connect(user3).withdraw(999997133560957503749n);
expect(await bank.connect(user3).checkBalance()).to.equal(0);
});
});

测试结果

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
  取款边界值测试
测试取款金额大于0的情况
✔ 应该允许取款金额为1(最小值)
✔ 应该允许取款金额为余额的一半
✔ 应该允许取款金额为余额-1
✔ 应该允许取款金额等于余额
✔ 应该拒绝取款金额为0
✔ 应该拒绝取款金额为负数(Solidity会自动处理)
测试账户余额大于取款金额的情况
✔ 余额100,取款50,应该成功
✔ 余额100,取款99,应该成功
✔ 余额100,取款100,应该成功
✔ 余额100,取款101,应该失败
✔ 余额100,取款最大值,应该失败
测试账户存在的情况
✔ 已创建账户的用户应该能取款
✔ 未创建账户的用户应该不能取款
✔ 创建账户但余额为0的用户应该不能取款
额外边界测试
✔ 连续多次取款
✔ 取款后再次存款再取款
✔ 测试大额取款


23 passing (1s)