0%

链上永存留言——修改版

1:永存留言

来一个好玩的项目来运行他,来学习他,这是比较有趣的方式。

一个非常简单的DAPP,永存留言。

来自:DAPP开发初探——永存的留言

里面的合约非常简单,复制到remix中编译运行即可。

编译错误将版本修改为:^0.4.26

我们简单解读一下:

1
2
3
4
5
6
// 留言结构体
struct Message {
string word; // 留言
address from; // 留言者地址
uint256 timestamp ; // 留言unix时间戳
}

首先,他申明了一个建构体,你可以理解为对象。

该对象有三个属性,留言的内容String,留言的钱包地址address,留言的时间unix。

1
Message[] private wordArr;

然后申明了一个对象数组,用他来存储留言信息。

如果是Java程序,那么该类停止运行就结束了,但是以太坊EVM并不会停止,所以可以一直运行

所以,把数据存储在链上,而不是MYSQL之类的数据库,这一点很重要。

2:改造方法

我们来看看他的两个方法,一个写入(付出GAS),一个读取(无需GAS):

1
2
3
4
5
6
7
function setWord(string s, string t) public {
wordArr.push(Message({
word: s,
from: msg.sender,
timestamp: t
}));
}

push是sol中数组的方法,表示在尾部添加元素。

该方法传入两个参数,分别表示:留言和时间。

这种赋值的方法在JavaScript中叫解构(ES6语法),此处异曲同工,各种语言相互借鉴。

但是,如果时间信息由用户来传递是否会造成一些问题呢?

所以,我自己将参数t去掉,赋值操作为:timestamp: now

now获取当前时间(查百度的)。

这样就只需要输入留言内容即可。

读取的方法如下:

1
2
3
4
5
6
7
8
function getRandomWord(uint random) public view returns (uint, string, address, uint256) {
if(wordArr.length==0){
return (0, "", msg.sender, 0);
}else{
Message storage result = wordArr[random];
return (wordArr.length, result.word, result.from, result.timestamp);
}
}

参数是random,如果对象数组没有留言就返回空,否则就返回数组中的random+1条留言。

这里有一个问题,如对象数组只有2条留言,我输入的random为10,他不会返回任何对象,这违背了程序设计的初衷。

所以,我们魔改一下:

1
2
3
4
5
6
7
8
9
10
function getRandomWord(uint random) public view returns (uint, string, address, uint256) {
if(wordArr.length==0){
return (0, "当前没有留言", 0x0000000000000000000000000000000000000000, 0);
}else if(random>=wordArr.length){
return (1,"超出留言范围,请重试!",0x0000000000000000000000000000000000000000,1);
}else{
Message storage result = wordArr[random];
return (wordArr.length, result.word, result.from, result.timestamp);
}
}

我们加上温馨的提示,最后把提示人改成:0x0000000000000000000000000000000000000000,完美。

3:新功能

我觉得只有一个读取方法未免不合理,所以我决定加几个。

比如,读取最新消息:

1
2
3
4
5
6
7
8
function getNewMessage() public view returns (uint, string, address, uint256) {
if(wordArr.length==0){
return (0, "当前没有留言", 0x0000000000000000000000000000000000000000, 0);
}else{
Message storage result = wordArr[wordArr.length-1];
return (wordArr.length, result.word, result.from, result.timestamp);
}
}

但是,只有这个也太无聊了,我们还写一个叫,读取指定用户所有留言信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function getAddressAllMessage(address add) public view returns (Message[] memory) {
// 设置返回的留言结构体
Message[] memory addMessage = new Message[](wordArr.length);
// 没有留言时默认返回内容
if(wordArr.length==0){
addMessage[0] = Message({
word: "当前没有留言",
from: 0x0000000000000000000000000000000000000000,
timestamp: 0
});
return addMessage;
}else{
// 将匹配的内容填入新结构体中
uint count = 0;
for(uint i=0; i<wordArr.length; i++){
if(add==wordArr[i].from){
addMessage[count] = wordArr[i];
count++;
}
}
return addMessage;
}
}

remix自带的ASK GPT帮助了我,因为我返回的时候一直报错。最后在顶部添加了一行:

1
pragma experimental ABIEncoderV2;

才算没有红色错误,只有黄色的警告,只要编译不出问题那就没有问题。

既然都有读取指定用户的所有留言,干脆出一个读取全部留言功能:

1
2
3
4
5
6
/**
* 获取所有留言的方法
*/
function getAllMessage() public view returns (Message[] memory) {
return wordArr;
}

我们就来部署一下。使用测试网来运行他:

https://sepolia.etherscan.io/address/0xc2755fdfa1ac3c8a98c3335003e179e40d29888f#readContract

image-20231102170238582

可以拿来偶尔写点东西,唉嘿。

旁边的Read Contract可以用来查询。