在登链社区挑战100天学习web3——第3天,solana dapp movies review 示例开发

  • zero.eth
  • 更新于 2024-08-30 11:09
  • 阅读 503

100 篇笔记学习 web3 之第三篇:solana dapp 示例开发

写在前面

姗姗来迟的第三天记录,因为一旦开始写代码就会遇到各种问题,需要花时间去了解,加上本身要工作,各种琐事也要花时间,还有懒……

本来是想写 100 天学习 web3 日记,但是目前看起来实在是太难了。

每天学一点无法总结成新的笔记,而且我还有全职工作,一天不一定能挤出来很多时间学习,也学不明白。

因此这个系列要改成 100 篇学习笔记了。

学到的东西

使用开发网络 devnet

  • 和钱包交互
  • 和链上的 movie review 程序交互

钱包交互

npx create-solana-dapp 创建项目模板,每一行代码都研究了,现在知道怎么创建一个 dapp 并且和钱包交互。

movie review 程序

通过这个练习,进一步学习了链上的数据如何存储和访问,能够和 web2 的数据库概念对上了。

数据账户和 pda

  • solana 上的程序就是 eth 上的智能合约
  • solana 上一切皆账户,普通的钱包是账户,程序也是账户
  • 程序是无状态的,不存储数据,数据单独用一个账户存,叫做 pda, Program Derived Address,没有私钥,只能由程序控制

程序如何存储特定用户(钱包地址)的数据?

在 web2 中,我们用数据库根据用户的 id 来存各种信息。

而区块链是分布式的,solana 的程序如何根据不同的用户(web3的用户就是钱包地址)来存对应的数据呢?

通过 findProgramAddressSync API,它的参数是 seedpublicKey。通常 publicKey 是程序账户的地址,而 seed 是一个 buffer 数组,可以根据需要进行组合。不同的 seed 搭配 publicKey 可以衍生出不同的 pda 地址,这些地址每一个都是 solana 上的账户,账户存的数据对应的就是 web2 的数据库。

比如存储特定用户数据的程序中,通常使用用户的公钥作为种子。这将每个用户的数据分离到自己的 PDA 中。

const [pda, bump] = await web3.PublicKey.findProgramAddressSync(
  [
    publicKey.toBuffer()
  ],
  programId
)

这样当我们需要查询用户数据时,使用 programId 和用户的公钥就可以方便的找到用户的数据。

也可以不指定用户条件,获取由程序创建的所有账户

const accounts = connection.getProgramAccounts(programId).then(accounts => {
  accounts.map(({ pubkey, account }) => {
    console.log('Account:', pubkey)
    console.log('Data buffer:', account.data)
  })
})

序列化和反序列化,偏移量

存的数据需要序列化然后存到区块链上,读取的时候自然要反序列化成对象。

字段的顺序非常重要,因为不同的字段类型有不同的长度,字段的顺序确定了偏移量。

solana 上的合约程序是用 rust 写的,了解偏移量实际是要了解 rust 中不同的数据类型的长度。

常见类型的长度:

  • boolean 1字节
  • u8 1字节
  • u16 2字节
  • f32 4字节
  • f64 8字节
  • str 长度不固定,但是前4字节用来记录字符串的长度,因此查找字符串时,偏移量要记得+4

序列化和反序列化时数据结构字段不一样,见这里说明

原因参见本身合约程序 rust 代码。

排序

根据 title 进行排序时,读取 title 需要计算好偏移量,由于不知道 title 具体的长度,因此会用一个大的 range 去尽可能读取 title 的全部,但是 title 有可能是空值,读取的时候又会报错

try {
    // 可能有的标题是空的,或者没有那么长,读取就会报错
    lengthA = a.account.data.readUInt32LE(0); // 由于 getProgramAccounts 的时候已经 dataSlice.offset=2 了,因此这里从0开始读取
} catch (e) { 
    // 标题不存在就排到后面去
    return 1
}

如果 title 都有值,那么就可以进行 buffer 的比较

buffer 的比较示例:

var buf1 = Buffer.from('abc');
var buf2 = Buffer.from('abc');
var x = Buffer.compare(buf1, buf2);
console.log(x); // x=0 字符串相等

var buf1 = Buffer.from('a');
var buf2 = Buffer.from('b');
var x = Buffer.compare(buf1, buf2);
console.log(x); // x=-1 a 在 b 的前面

var buf1 = Buffer.from('b');
var buf2 = Buffer.from('a');
var x = Buffer.compare(buf1, buf2);
console.log(x);// x=1 b 在 a 的后面

比较 title 时也要注意偏移量,title 的类型在 rust 中应该是字符串的胖指针,前4个字节存字符串的具体长度,因此比较字符串具体内容应当+4

if (lengthA && lengthB) {
    console.log('排序')
    const dataA = a.account.data.subarray(4, 4 + lengthA);
    const dataB = b.account.data.subarray(4, 4 + lengthB);

    return dataA.compare(dataB);
}

搜索

当搜索条件变化后,要重新去链上查找数据。

遇到的问题

  1. 基于 wsl 的 Ubuntu 系统上运行 nextjs 项目,项目的文件路径会影响热更新功能。

如果是 /mnt/xxx 路径,热更新不会生效,但是能在 vscode 中写代码

如果是 ~/xxx 路径,热更新会生效。但是无法找到具体的 windows 文件映射地址,从而在 vscode 中写代码(Ubuntu 也能安装 vscode,命令行输入 code 会自动安装,code xxx 就能正常编辑项目写代码了,热更新也没问题。)

  1. 无法访问 solana.com 域名

因为网络关系,无法访问 .solana.com 域名,同样的也无法在代码中使用 clusterApiUrl('devnet'),只能用其他 rpc 地址。

但是在与 phantom 钱包交互,发送签名时,感觉测试不了 devnet,模拟交易失败,因此无法更进一步调试代码。

因此选择跳过和钱包交互,不使用钱包的 sendTransaction API 而是使用 @solana/web3.jssendAndConfirmTransaction API 来发送和确认交易

点赞 1
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
zero.eth
zero.eth
0xD736...60aa
江湖只有他的大名,没有他的介绍。