100 篇笔记学习 web3 之第三篇:solana dapp 示例开发
姗姗来迟的第三天记录,因为一旦开始写代码就会遇到各种问题,需要花时间去了解,加上本身要工作,各种琐事也要花时间,还有懒……
本来是想写 100 天学习 web3 日记,但是目前看起来实在是太难了。
每天学一点无法总结成新的笔记,而且我还有全职工作,一天不一定能挤出来很多时间学习,也学不明白。
因此这个系列要改成 100 篇学习笔记了。
使用开发网络 devnet
npx create-solana-dapp
创建项目模板,每一行代码都研究了,现在知道怎么创建一个 dapp 并且和钱包交互。
通过这个练习,进一步学习了链上的数据如何存储和访问,能够和 web2 的数据库概念对上了。
数据账户和 pda
Program Derived Address
,没有私钥,只能由程序控制程序如何存储特定用户(钱包地址)的数据?
在 web2 中,我们用数据库根据用户的 id 来存各种信息。
而区块链是分布式的,solana 的程序如何根据不同的用户(web3的用户就是钱包地址)来存对应的数据呢?
通过 findProgramAddressSync
API,它的参数是 seed
和 publicKey
。通常 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 中不同的数据类型的长度。
常见类型的长度:
序列化和反序列化时数据结构字段不一样,见这里说明
原因参见本身合约程序 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);
}
搜索
当搜索条件变化后,要重新去链上查找数据。
如果是 /mnt/xxx 路径,热更新不会生效,但是能在 vscode 中写代码
如果是 ~/xxx 路径,热更新会生效。但是无法找到具体的 windows 文件映射地址,从而在 vscode 中写代码(Ubuntu 也能安装 vscode,命令行输入 code 会自动安装,code xxx 就能正常编辑项目写代码了,热更新也没问题。)
因为网络关系,无法访问 .solana.com
域名,同样的也无法在代码中使用 clusterApiUrl('devnet')
,只能用其他 rpc 地址。
但是在与 phantom 钱包交互,发送签名时,感觉测试不了 devnet,模拟交易失败,因此无法更进一步调试代码。
因此选择跳过和钱包交互,不使用钱包的 sendTransaction
API 而是使用 @solana/web3.js
的 sendAndConfirmTransaction
API 来发送和确认交易
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!