1 个不稳定版本
0.1.0 | 2024年5月24日 |
---|
#21 in #modal
2MB
9K SLoC
WalletConnect Modal 绑定的 WASM 抽象
这是为 WalletConnect 的 Modal 创建的绑定抽象。使用 cargo doc --target wasm32-unknown-unknown --open
可以提供有关使用此库的信息。
为什么会有这个存在呢?
使用 WalletConnect 与 Rust WASM 前端似乎很复杂,我想为我的项目获取绑定。
这个抽象的目标仅仅是使 Rust 基于以太坊/区块链提供者(如 ethers-rs、alloy-rs、web3-rs 等)轻松使用 WalletConnect 的 Modal。尽管目前这个包仅支持 ethers-rs。这已经有一段时间没有写了,所以 NPM 中的包也不是新的。(我也有一段时间没有搜索可能具有相同用途的其他库了)
为什么要以这种方式编写呢?
我用编写这个库来尝试 Rust。
编写它的一个目标是将 JS 中的动态类型转换为 Rust 中的可靠和稳固的类型。我认为它在内部过于复杂,我最初尝试封装 JsValue 的方法难以理解。
包含内部 JsValue 值的结构无法直接与 ethers、alloy 和其他提供者的 trait 要求进行交互,因为它们需要 Send + Sync 限制。在没有现有运行时的情况下,一个安全的 Rust 解决方案是使用异步通道库和运行时/服务器来监听这些消息。这正是这个包所做的事情,它充当运行时/服务器,并将提供者请求发送的消息传递给从基于 Rust 的提供者传递的 trait 限制。
当前版本执行对 JS 绑定 RPC 提供者的 JSON 请求看起来如下:
let request = RequestFromTransport {
full_rpc_request: json_request,
queue_type: queue_type,
send_back_to: send,
started: false,
start_time: None,
queue_time: Utc::now(),
};
if let Err(_e) = server_cmd_cli
.send(ClientCommands::RpcRequest(request))
.await
{
return Err(WalTransportFailure {
msg: "Channel closed. This should never happen. Open an issue: {_e:?}".into(),
failure: WalletConnectTransportFailure::Json(JsonFailure::JsDe),
});
}
if let Ok(result) = receieve.recv().await {
result
} else {
return Err(WalTransportFailure {
msg: format!("Channel closed. This should never happen. Open an issue"),
failure: WalletConnectTransportFailure::Json(JsonFailure::JsDe),
});
}
这有一个相关的运行时/服务器,它作为后台中的 promise 运行。
loop {
let next_cmd = self.command_receiver_channel.recv().await.unwrap();
match next_cmd {
ClientCommands::RpcRequest(req) => {
if self.rpc_runner.has_active_request() {
match req.queue_type {
QueueTypes::UserKillRequest => {
let _ = self.rpc_runner.send_request_kill_signal().await;
// User requests can quickly change before being finished. If a user changes
// their request, then other user requests should just be cancelled.
if let Some(next_kill_req) = &self.active_kill_request_next {
let _ = next_kill_req.kill().await;
}
self.active_kill_request_next = Some(req);
}
QueueTypes::SystemQueueRequest => {
self.rpc_request_queue.push_back(req);
}
}
} else {
let _ = self.rpc_runner.send_request(req).await;
}
}
/* The rest of the matched types are also included here, but removed in this description */
}
}
在写完所有这些后,我意识到这可以通过一个包装类型来实现简化,该类型使用闭包完成所有这些操作。因此,我提取了与运行时/服务器相关的内容,创建了一个自定义类型 JsArc,我认为这将有助于降低复杂性。在RPC请求的例子中,上述内容可能如下所示:
let (s, r) = async_channel::unbounded();
let request: String = request;
self.jswallet.with_self_async(|jswallet| async move {
let result = jswallet.request(request).await;
s.send(result.to_string().unwrap()).await;
jswallet
})
.await;
let result: String = r.recv().await;
尽管如此,我没有修复绑定抽象,因为我已经为所有内容编写了消息发送和接收。如果我现在再写一遍,我认为我会直接使用那个包装的JsValue类型,而不是将消息传递到自定义运行时/服务器。
其余的复杂性主要与实验相关
- 我尝试编写宏,因为提供者必须手动克隆才能移动到自定义future
- 我尝试创建队列,因为我感觉这可能是一个很好的步骤,未来库可以使用它来知道何时取消钱包的请求
- 我尝试创建rust提供者错误的共享格式
- 我希望下游用户能够选择不手动管理NPM依赖项,因此这个crate使用了一个预捆绑的版本。(但用户也有选择使用带有功能标志
npm
的npm)- 从web链接模块到动态模块的更改导致原始绑定不匹配。(并且留下了旧绑定的部分)
构建
使用示例
wasm-pack build --target web
cargo install miniserve
miniserve .
Miniserve在当前目录启动一个web服务器。它将显示一个要访问的地址,然后在该地址加载index.html文件。
构建新的web3_bindings.bundle.js
cd js
npm install
cd bindings
npx webpack
您需要安装NPM和Webpack才能使上述操作正常工作。
依赖项
~35–52MB
~1M SLoC