From 84561f8f595810177a5de7f215b0a9d8da540975 Mon Sep 17 00:00:00 2001 From: nickwest Date: Sat, 7 Oct 2023 00:24:18 -0700 Subject: [PATCH 1/6] add wasm examples of tx --- bindings/wasm/Cargo.toml | 14 +- bindings/wasm/src/crypto.rs | 27 +- bindings/wasm/src/lib.rs | 7 +- bindings/wasm/src/provider.rs | 70 +++++ bindings/wasm/src/rpc.rs | 121 ++++++++ bindings/wasm/src/tx_types/change_pubkey.rs | 100 +++++++ bindings/wasm/src/tx_types/mod.rs | 2 + bindings/wasm/src/tx_types/transfer.rs | 78 ++++++ bindings/wasm/tests/test_provider.rs | 107 +++++++ .../Javascript/js-example/1_change_pubkey.js | 65 +++++ examples/Javascript/js-example/2_transfer.js | 35 +++ examples/Javascript/js-example/index.html | 2 +- provider/Cargo.toml | 14 +- provider/src/error.rs | 16 ++ provider/src/lib.rs | 8 + provider/src/network.rs | 14 + provider/src/rpc_wasm.rs | 262 ++++++++++++++++++ signers/Cargo.toml | 2 - signers/src/eth_signer/eip1271_signature.rs | 2 +- signers/src/eth_signer/mod.rs | 3 +- types/src/tx_type/mod.rs | 7 + 21 files changed, 926 insertions(+), 30 deletions(-) create mode 100644 bindings/wasm/src/provider.rs create mode 100644 bindings/wasm/src/rpc.rs create mode 100644 bindings/wasm/src/tx_types/change_pubkey.rs create mode 100644 bindings/wasm/src/tx_types/mod.rs create mode 100644 bindings/wasm/src/tx_types/transfer.rs create mode 100644 bindings/wasm/tests/test_provider.rs create mode 100644 examples/Javascript/js-example/1_change_pubkey.js create mode 100644 examples/Javascript/js-example/2_transfer.js create mode 100644 provider/src/error.rs create mode 100644 provider/src/rpc_wasm.rs diff --git a/bindings/wasm/Cargo.toml b/bindings/wasm/Cargo.toml index ee938e76..0ef83b3d 100644 --- a/bindings/wasm/Cargo.toml +++ b/bindings/wasm/Cargo.toml @@ -5,22 +5,26 @@ edition = "2021" publish = false [lib] -crate-type = ["cdylib"] +crate-type = ["cdylib","rlib"] [dependencies] zklink_sdk_signers = { path = "../../signers" } zklink_sdk_types = { path = "../../types" } +zklink_sdk_provider = { path = "../../provider" } wasm-bindgen = { version = "0.2.87",features = ["serde-serialize"] } +serde-wasm-bindgen = "0.5" getrandom = { version = "0.2.10", features = ["js"] } +web-sys = "0.3" +hex = "0.4.3" + +[target.'cfg(target_arch = "wasm32")'.dependencies] +wasm-bindgen-futures = "0.4" [features] default = [] ffi = [] [dev-dependencies] -actix-rt = "2.7" -actix-web = "4.2" -futures = "0.3" -tokio = { version = "1", features = ["full"] } +wasm-bindgen-test = "0.3" [package.metadata.wasm-pack.profile.release] wasm-opt = false diff --git a/bindings/wasm/src/crypto.rs b/bindings/wasm/src/crypto.rs index 335443f9..3aab81be 100644 --- a/bindings/wasm/src/crypto.rs +++ b/bindings/wasm/src/crypto.rs @@ -1,10 +1,7 @@ use wasm_bindgen::prelude::*; +use zklink_sdk_signers::eth_signer::pk_signer::EthSigner; use zklink_sdk_signers::zklink_signer::pk_signer::ZkLinkSigner as Signer; -// use zklink_sdk_signers::zklink_signer::error::ZkSignerError; use zklink_sdk_signers::zklink_signer::signature::ZkLinkSignature as Signature; -// use zklink_sdk_signers::eth_signer::eth_signature::TxEthSignature; -// use wasm_bindgen_futures::{JsFuture, future_to_promise}; -use zklink_sdk_signers::eth_signer::pk_signer::EthSigner; #[wasm_bindgen] pub struct EthPrivateKeySigner { @@ -13,18 +10,18 @@ pub struct EthPrivateKeySigner { #[wasm_bindgen] impl EthPrivateKeySigner { - #[wasm_bindgen] + #[wasm_bindgen(js_name=newFromHexPrivateKey)] pub fn new_from_hex_pk(private_key: &str) -> Result { let signer = EthSigner::try_from(private_key)?; Ok(Self { inner: signer }) } - #[wasm_bindgen] - pub fn get_address(&self) -> Result { - let address = self.inner.get_address()?; - Ok(format!("{:?}", address)) + #[wasm_bindgen(js_name=getAddress)] + pub fn get_address(&self) -> String { + let address = self.inner.get_address(); + format!("{:?}", address) } - #[wasm_bindgen] + #[wasm_bindgen(js_name=signMessage)] pub fn sign_message(&self, msg: &[u8]) -> Result { let signature = self.inner.sign_message(msg)?; Ok(signature.as_hex()) @@ -38,7 +35,7 @@ pub struct ZklinkSigner { #[wasm_bindgen] impl ZklinkSigner { - #[wasm_bindgen(js_name=NewRand)] + #[wasm_bindgen(js_name=newRand)] pub fn new_rand() -> Result { let zklink_signer = Signer::new()?; Ok(ZklinkSigner { @@ -46,7 +43,7 @@ impl ZklinkSigner { }) } - #[wasm_bindgen(js_name=NewFromEthSigner)] + #[wasm_bindgen(js_name=newFromEthSigner)] pub fn new_from_hex_eth_signer(eth_hex_private_key: &str) -> Result { let zklink_signer = Signer::new_from_hex_eth_signer(eth_hex_private_key)?; Ok(ZklinkSigner { @@ -68,13 +65,13 @@ pub struct ZklinkSignature { #[wasm_bindgen] impl ZklinkSignature { - #[wasm_bindgen(js_name=NewFromHexStr)] + #[wasm_bindgen(js_name=newFromHexStr)] pub fn new_from_hex_str(signature_str: &str) -> Result { let signature = Signature::from_hex(signature_str)?; Ok(ZklinkSignature { inner: signature }) } #[wasm_bindgen] - pub fn verify(&self, msg: &[u8]) -> Result { - Ok(self.inner.verify_musig(msg)?) + pub fn verify(&self, msg: &[u8]) -> bool { + self.inner.verify_musig(msg) } } diff --git a/bindings/wasm/src/lib.rs b/bindings/wasm/src/lib.rs index c93b8562..1a366db5 100644 --- a/bindings/wasm/src/lib.rs +++ b/bindings/wasm/src/lib.rs @@ -1,8 +1,9 @@ -#[cfg(target_arch = "wasm32")] +#![cfg(target_arch = "wasm32")] pub mod crypto; -// #[cfg(not(target_arch = "wasm32"))] +pub mod provider; +pub mod rpc; +pub mod tx_types; // pub mod wallet; -// #[cfg(not(target_arch = "wasm32"))] // pub mod error; extern crate getrandom; diff --git a/bindings/wasm/src/provider.rs b/bindings/wasm/src/provider.rs new file mode 100644 index 00000000..b69657fa --- /dev/null +++ b/bindings/wasm/src/provider.rs @@ -0,0 +1,70 @@ +use crate::rpc::{AccountQueryParam, SignedTransaction, TxL1Signature}; +use std::collections::HashMap; +use std::str::FromStr; +use wasm_bindgen::prelude::wasm_bindgen; +use wasm_bindgen::JsValue; +use zklink_sdk_provider::network::Network; +use zklink_sdk_provider::response::{AccountSnapshotResp, TokenResp}; +use zklink_sdk_provider::rpc_wasm::WasmRpcClient; +use zklink_sdk_signers::zklink_signer::ZkLinkSignature; +use zklink_sdk_types::basic_types::tx_hash::TxHash; +use zklink_sdk_types::basic_types::{BlockNumber, SubAccountId, TokenId}; + +#[wasm_bindgen] +pub struct Provider { + client: WasmRpcClient, +} + +#[wasm_bindgen] +impl Provider { + #[wasm_bindgen(constructor)] + pub fn new(network: &str) -> Provider { + Provider { + client: WasmRpcClient { + server_url: Network::from_str(network).unwrap().url().to_owned(), + }, + } + } + + #[wasm_bindgen] + pub async fn tokens(&self) -> Result { + let result: HashMap = self.client.tokens().await?; + Ok(serde_wasm_bindgen::to_value(&result)?) + } + + #[wasm_bindgen(js_name=accountQuery)] + pub async fn account_query( + &self, + account_query: AccountQueryParam, + sub_account_id: Option, + block_number: Option, + ) -> Result { + let result: AccountSnapshotResp = self + .client + .account_query( + account_query.into(), + sub_account_id.map(|id| SubAccountId(id)), + block_number.map(|number| BlockNumber(number)), + ) + .await?; + Ok(serde_wasm_bindgen::to_value(&result)?) + } + + #[wasm_bindgen(js_name=sendTransaction)] + pub async fn send_transaction( + &self, + tx: SignedTransaction, + l1_signature: Option, + l2_signature: Option, + ) -> Result { + let result: TxHash = self + .client + .send_transaction( + tx.into(), + l1_signature.map(|t| t.into()), + l2_signature.map(|s| ZkLinkSignature::from_hex(&s).unwrap()), + ) + .await?; + Ok(result.as_hex()) + } +} diff --git a/bindings/wasm/src/rpc.rs b/bindings/wasm/src/rpc.rs new file mode 100644 index 00000000..92826c5f --- /dev/null +++ b/bindings/wasm/src/rpc.rs @@ -0,0 +1,121 @@ +use std::str::FromStr; +use wasm_bindgen::prelude::wasm_bindgen; +use wasm_bindgen::JsValue; +use zklink_sdk_provider::response::AccountQuery; +use zklink_sdk_signers::eth_signer::{EIP1271Signature, PackedEthSignature}; +use zklink_sdk_signers::starknet_signer::StarkECDSASignature; +use zklink_sdk_types::basic_types::AccountId; +use zklink_sdk_types::prelude::ZkLinkAddress; +use zklink_sdk_types::signatures::TxLayer1Signature; +use zklink_sdk_types::tx_type::change_pubkey::ChangePubKey; +use zklink_sdk_types::tx_type::transfer::Transfer; +use zklink_sdk_types::tx_type::zklink_tx::ZkLinkTx; + +#[wasm_bindgen] +#[derive(Copy, Clone)] +pub enum AccountQueryType { + AccountId, + Address, +} + +#[wasm_bindgen] +#[derive(Copy, Clone)] +pub enum L1SignatureType { + Eth, + Eip1271, + Stark, +} + +#[wasm_bindgen] +pub struct AccountQueryParam { + query_type: AccountQueryType, + query_param: String, +} + +#[wasm_bindgen] +pub struct TxL1Signature { + sign_type: L1SignatureType, + signature: String, +} + +#[wasm_bindgen] +pub struct SignedTransaction { + tx_type: u8, + tx: JsValue, +} + +#[wasm_bindgen] +impl AccountQueryParam { + #[wasm_bindgen(constructor)] + pub fn new(query_type: AccountQueryType, query_param: String) -> AccountQueryParam { + AccountQueryParam { + query_type, + query_param, + } + } +} + +impl From for AccountQuery { + fn from(query: AccountQueryParam) -> AccountQuery { + match query.query_type { + AccountQueryType::AccountId => { + AccountQuery::Id(AccountId(query.query_param.parse::().unwrap())) + } + AccountQueryType::Address => { + AccountQuery::Address(ZkLinkAddress::from_str(&query.query_param).unwrap()) + } + } + } +} + +#[wasm_bindgen] +impl TxL1Signature { + #[wasm_bindgen] + pub fn new(sign_type: L1SignatureType, signature: String) -> TxL1Signature { + TxL1Signature { + sign_type, + signature, + } + } +} + +impl From for TxLayer1Signature { + fn from(signature: TxL1Signature) -> TxLayer1Signature { + match signature.sign_type { + L1SignatureType::Eth => TxLayer1Signature::EthereumSignature( + PackedEthSignature::from_hex(&signature.signature).unwrap(), + ), + L1SignatureType::Eip1271 => TxLayer1Signature::EIP1271Signature(EIP1271Signature( + hex::decode(signature.signature).unwrap(), + )), + L1SignatureType::Stark => TxLayer1Signature::StarkSignature(StarkECDSASignature( + hex::decode(signature.signature).unwrap(), + )), + } + } +} + +#[wasm_bindgen] +impl SignedTransaction { + #[wasm_bindgen(constructor)] + pub fn new(tx_type: u8, tx: JsValue) -> SignedTransaction { + SignedTransaction { tx_type, tx } + } +} +impl From for ZkLinkTx { + fn from(tx: SignedTransaction) -> ZkLinkTx { + match tx.tx_type { + ChangePubKey::TX_TYPE => { + let change_pubkey: ChangePubKey = serde_wasm_bindgen::from_value(tx.tx).unwrap(); + ZkLinkTx::ChangePubKey(Box::new(change_pubkey)) + } + Transfer::TX_TYPE => { + let transfer: Transfer = serde_wasm_bindgen::from_value(tx.tx).unwrap(); + ZkLinkTx::Transfer(Box::new(transfer)) + } + _ => { + panic!("Not support tx type!") + } + } + } +} diff --git a/bindings/wasm/src/tx_types/change_pubkey.rs b/bindings/wasm/src/tx_types/change_pubkey.rs new file mode 100644 index 00000000..96aaec6a --- /dev/null +++ b/bindings/wasm/src/tx_types/change_pubkey.rs @@ -0,0 +1,100 @@ +use crate::crypto::ZklinkSigner; +use std::str::FromStr; +use wasm_bindgen::prelude::wasm_bindgen; +use wasm_bindgen::JsValue; +use zklink_sdk_signers::eth_signer::packed_eth_signature::PackedEthSignature; +use zklink_sdk_signers::zklink_signer::pubkey_hash::PubKeyHash; +use zklink_sdk_types::basic_types::{AccountId, BigUint, TokenId, ZkLinkAddress}; +use zklink_sdk_types::prelude::{ChainId, Nonce, SubAccountId, TimeStamp, ZkLinkSignature}; +use zklink_sdk_types::tx_builder::ChangePubKeyBuilder; +use zklink_sdk_types::tx_type::change_pubkey::ChangePubKey as ChangePubkeyTx; +use zklink_sdk_types::tx_type::TxTrait; + +#[wasm_bindgen] +pub enum EthAuthType { + OnChain, + EthECDSA, + EthCREATE2, +} + +#[wasm_bindgen] +pub struct ChangePubKey { + inner: ChangePubkeyTx, +} + +#[wasm_bindgen] +impl ChangePubKey { + #[wasm_bindgen(constructor)] + pub fn new( + chain_id: u8, + account_id: u32, + sub_account_id: u8, + new_pubkey_hash: String, + fee_token: u32, + fee: String, + nonce: u32, + eth_signature: Option, + ts: u32, + ) -> Result { + let eth_signature = if let Some(signature) = eth_signature { + Some(PackedEthSignature::from_hex(&signature)?) + } else { + None + }; + let change_pubkey_builder = ChangePubKeyBuilder { + chain_id: ChainId(chain_id), + account_id: AccountId(account_id), + sub_account_id: SubAccountId(sub_account_id), + new_pubkey_hash: PubKeyHash::from_hex(&new_pubkey_hash).unwrap(), + fee_token: TokenId(fee_token), + fee: BigUint::from_str(&fee).unwrap(), + nonce: Nonce(nonce), + eth_signature, + timestamp: TimeStamp(ts), + }; + Ok(ChangePubKey { + inner: ChangePubkeyTx::new(change_pubkey_builder), + }) + } + + #[wasm_bindgen(js_name=getTxType)] + pub fn get_tx_type(&self) -> u8 { + ChangePubkeyTx::TX_TYPE + } + + #[wasm_bindgen(js_name=getTx)] + pub fn get_tx(&mut self) -> JsValue { + serde_wasm_bindgen::to_value(&self.inner).unwrap() + } + + #[wasm_bindgen] + pub fn sign(&mut self, signer: &mut ZklinkSigner) -> Result { + let msg = self.inner.get_bytes(); + Ok(signer.sign(&msg)?) + } + + #[wasm_bindgen(js_name = getChangePubkeyMessage)] + pub fn get_change_pubkey_message( + &self, + layer_one_chain_id: u32, + verifying_contract: String, + ) -> Result { + let contract = ZkLinkAddress::from_str(&verifying_contract).unwrap_or_default(); + let typed_data = self + .inner + .to_eip712_request_payload(layer_one_chain_id, &contract)?; + Ok(typed_data.raw_data) + } + + #[wasm_bindgen(js_name = setL2Signature)] + pub fn set_zklink_signature(&mut self, signature: String) -> Result<(), JsValue> { + self.inner.signature = ZkLinkSignature::from_hex(&signature)?; + Ok(()) + } + + #[wasm_bindgen(js_name = submitterSign)] + pub fn submitter_sign(&mut self, signer: &mut ZklinkSigner) -> Result { + let tx_hash = self.inner.tx_hash(); + Ok(signer.sign(&tx_hash)?) + } +} diff --git a/bindings/wasm/src/tx_types/mod.rs b/bindings/wasm/src/tx_types/mod.rs new file mode 100644 index 00000000..21c79913 --- /dev/null +++ b/bindings/wasm/src/tx_types/mod.rs @@ -0,0 +1,2 @@ +pub mod change_pubkey; +pub mod transfer; diff --git a/bindings/wasm/src/tx_types/transfer.rs b/bindings/wasm/src/tx_types/transfer.rs new file mode 100644 index 00000000..30fa635c --- /dev/null +++ b/bindings/wasm/src/tx_types/transfer.rs @@ -0,0 +1,78 @@ +use crate::crypto::ZklinkSigner; +use std::str::FromStr; +use wasm_bindgen::prelude::wasm_bindgen; +use wasm_bindgen::JsValue; +use zklink_sdk_types::basic_types::{AccountId, BigUint, TokenId, ZkLinkAddress}; +use zklink_sdk_types::prelude::{Nonce, SubAccountId, TimeStamp, ZkLinkSignature}; +use zklink_sdk_types::tx_builder::TransferBuilder; +use zklink_sdk_types::tx_type::transfer::Transfer as TransferTx; +use zklink_sdk_types::tx_type::TxTrait; + +#[wasm_bindgen] +pub struct Transfer { + inner: TransferTx, +} + +#[wasm_bindgen] +impl Transfer { + #[wasm_bindgen(constructor)] + pub fn new( + account_id: u32, + to_address: String, + from_sub_account_id: u8, + to_sub_account_id: u8, + token: u32, + fee: String, + amount: String, + nonce: u32, + ts: u32, + ) -> Result { + let transfer_builder = TransferBuilder { + account_id: AccountId(account_id), + to_address: ZkLinkAddress::from_str(&to_address).unwrap(), + from_sub_account_id: SubAccountId(from_sub_account_id), + to_sub_account_id: SubAccountId(to_sub_account_id), + token: TokenId(token), + fee: BigUint::from_str(&fee).unwrap(), + nonce: Nonce(nonce), + timestamp: TimeStamp(ts), + amount: BigUint::from_str(&amount).unwrap(), + }; + Ok(Transfer { + inner: TransferTx::new(transfer_builder), + }) + } + + #[wasm_bindgen] + pub fn sign(&mut self, signer: &mut ZklinkSigner) -> Result { + let msg = self.inner.get_bytes(); + Ok(signer.sign(&msg)?) + } + + #[wasm_bindgen(js_name = getEthSignMessage)] + pub fn get_eth_sign_message(&self, token_symbol: String) -> String { + self.inner.get_eth_sign_msg(&token_symbol) + } + + #[wasm_bindgen(js_name=getTxType)] + pub fn get_tx_type(&self) -> u8 { + TransferTx::TX_TYPE + } + + #[wasm_bindgen(js_name=getTx)] + pub fn get_tx(&mut self) -> JsValue { + serde_wasm_bindgen::to_value(&self.inner).unwrap() + } + + #[wasm_bindgen(js_name = setL2Signature)] + pub fn set_zklink_signature(&mut self, signature: String) -> Result<(), JsValue> { + self.inner.signature = ZkLinkSignature::from_hex(&signature)?; + Ok(()) + } + + #[wasm_bindgen(js_name = submitterSign)] + pub fn submitter_sign(&mut self, signer: &mut ZklinkSigner) -> Result { + let tx_hash = self.inner.tx_hash(); + Ok(signer.sign(&tx_hash)?) + } +} diff --git a/bindings/wasm/tests/test_provider.rs b/bindings/wasm/tests/test_provider.rs new file mode 100644 index 00000000..aa70ad1b --- /dev/null +++ b/bindings/wasm/tests/test_provider.rs @@ -0,0 +1,107 @@ +#![cfg(target_arch = "wasm32")] +use std::str::FromStr; +use wasm_bindgen::JsValue; +use wasm_bindgen_test::wasm_bindgen_test; +use wasm_bindgen_test::wasm_bindgen_test_configure; +use zklink_sdk_provider::rpc_wasm::WasmRpcClient; +use zklink_sdk_signers::eth_signer::EthSigner; +use zklink_sdk_signers::zklink_signer::{PubKeyHash, ZkLinkSigner}; +use zklink_sdk_types::basic_types::{AccountId, BigUint, ChainId, Nonce, TokenId, ZkLinkAddress}; +use zklink_sdk_types::prelude::{SubAccountId, TimeStamp}; +use zklink_sdk_types::tx_builder::ChangePubKeyBuilder; +use zklink_sdk_types::tx_type::change_pubkey::ChangePubKey; +use zklink_sdk_types::tx_type::zklink_tx::ZkLinkTx; +use zklink_sdk_types::tx_type::ZkSignatureTrait; +use zklink_sdk_wasm::provider::Provider; +use zklink_sdk_wasm::rpc::{AccountQueryParam, AccountQueryType, SignedTransaction}; + +wasm_bindgen_test_configure!(run_in_worker); +#[wasm_bindgen_test] +async fn test_get_tokens() { + web_sys::console::log_1(&JsValue::from_str("123")); + let client = WasmRpcClient::new("https://api-v1.zk.link".to_owned()); + let ret = client.tokens().await; + if let Err(e) = ret { + web_sys::console::log_1(&JsValue::from_str(&format!("{:?}", e))); + } else { + web_sys::console::log_1(&JsValue::from_str(&format!("{:?}", ret.unwrap()))); + } + // assert!(ret.is_err()); +} + +#[wasm_bindgen_test] +async fn test_account_query() { + let provider = Provider::new("testnet"); + let account_id = AccountQueryParam::new(AccountQueryType::AccountId, "5".to_string()); + let account_resp = provider.account_query(account_id.into(), None, None).await; + if let Err(e) = account_resp { + web_sys::console::log_1(&JsValue::from_str(&format!("{:?}", e))); + } else { + web_sys::console::log_1(&JsValue::from_str(&format!("{:?}", account_resp.unwrap()))); + } +} + +#[wasm_bindgen_test] +async fn test_send_change_pubkey() { + web_sys::console::log_1(&JsValue::from_str("123")); + let provider = Provider::new("testnet"); + let private_key = "be725250b123a39dab5b7579334d5888987c72a58f4508062545fe6e08ca94f4"; + let eth_signer = EthSigner::try_from(private_key).unwrap(); + let zklink_signer = ZkLinkSigner::new_from_hex_eth_signer(private_key).unwrap(); + let main_contract = "0x5505a8cD4594Dbf79d8C59C0Df1414AB871CA896"; + let l1_client_id = 80001; + let new_pubkey_hash = "0xd8d5fb6a6caef06aa3dc2abdcdc240987e5330fe"; + let ts = 1696595303; + //auth type 'ECDSA' + let builder = ChangePubKeyBuilder { + chain_id: ChainId(1), + account_id: AccountId(10), + sub_account_id: SubAccountId(1), + new_pubkey_hash: PubKeyHash::from_hex(new_pubkey_hash).unwrap(), + fee_token: TokenId(18), + fee: BigUint::from(100000000000000u64), + nonce: Nonce(1), + eth_signature: None, + timestamp: TimeStamp(ts), + }; + let mut change_pubkey = ChangePubKey::new(builder); + let message = change_pubkey + .to_eip712_request_payload( + l1_client_id, + &ZkLinkAddress::from_str(&main_contract).unwrap(), + ) + .unwrap(); + let signature = eth_signer + .sign_message(message.raw_data.as_bytes()) + .unwrap(); + let builder_with_sig = ChangePubKeyBuilder { + chain_id: ChainId(1), + account_id: AccountId(10), + sub_account_id: SubAccountId(1), + new_pubkey_hash: PubKeyHash::from_hex(new_pubkey_hash).unwrap(), + fee_token: TokenId(18), + fee: BigUint::from(100000000000000u64), + nonce: Nonce(1), + eth_signature: Some(signature), + timestamp: TimeStamp(ts), + }; + let mut tx = ChangePubKey::new(builder_with_sig); + tx.sign(&zklink_signer).unwrap(); + let submitter_signature = tx.signature.as_hex(); + //send to zklink + let ret = provider + .send_transaction( + SignedTransaction::new( + ChangePubKey::TX_TYPE, + serde_wasm_bindgen::to_value(&tx).unwrap(), + ), + None, + Some(l2_signature), + ) + .await; + if let Err(e) = ret { + web_sys::console::log_1(&JsValue::from_str(&format!("{:?}", e))); + } else { + web_sys::console::log_1(&JsValue::from_str(&format!("{:?}", ret.unwrap()))); + } +} diff --git a/examples/Javascript/js-example/1_change_pubkey.js b/examples/Javascript/js-example/1_change_pubkey.js new file mode 100644 index 00000000..60cd6f2a --- /dev/null +++ b/examples/Javascript/js-example/1_change_pubkey.js @@ -0,0 +1,65 @@ +import init, * as wasm from "./web-dist/zklink-sdk-web.js"; + +async function getChangePubkeyData( + auth_type, + main_contract, + l1_client_id, + chain_id, + account_id, + sub_account_id, + new_pubkey_hash, + fee_token, + fee, + nonce, + ts, +) { + // var authData: wasm.; + // if (auth_type == 'OnChain') { + // + // } else if (auth_type == '') { + // + // } else { + // + // } + console.log("getChangePubkeyData"); + let tx = new wasm.ChangePubKey(chain_id, account_id, sub_account_id, new_pubkey_hash, fee_token, fee, nonce, null, ts); + let message = tx.getChangePubkeyMessage(l1_client_id,main_contract); + console.log(message); + return message; +} +async function main() { + await init(); + const private_key = "be725250b123a39dab5b7579334d5888987c72a58f4508062545fe6e08ca94f4"; + const eth_signer = wasm.EthPrivateKeySigner.newFromHexPrivateKey(private_key); + const zklink_signer = wasm.ZklinkSigner.newFromEthSigner(private_key); + const main_contract = "0x5505a8cD4594Dbf79d8C59C0Df1414AB871CA896"; + const l1_client_id = 80001; + const new_pubkey_hash = "0xd8d5fb6a6caef06aa3dc2abdcdc240987e5330fe"; + const ts = Math.floor(Date.now() / 1000); + //auth type 'ECDSA' + let message = getChangePubkeyData('EthECDSA',main_contract,l1_client_id,1,10,1,new_pubkey_hash,18,"100000000000000",1,ts); + const msg = new TextEncoder().encode(message); + try { + let signature = eth_signer.signMessage(msg); + console.log(signature); + let tx = new wasm.ChangePubKey(1, 10, 1, new_pubkey_hash, 18, "100000000000000", 1, signature, ts); + let tx_signature = tx.sign(zklink_signer); + console.log(tx_signature); + tx.setL2Signature(tx_signature); + //send to zklink + let provder = new wasm.Provider("testnet"); + let signed_tx = new wasm.SignedTransaction(tx.getTxType(),tx.getTx()); + console.log(signed_tx); + let l1_signature = new wasm.TxL1Signature(wasm.L1SignatureType.Eth,signature); + let submitter_signature = tx.submitterSign(zklink_signer); + console.log(submitter_signature); + let tx_hash = await provder.sendTransaction(signed_tx,l1_signature,submitter_signature); + console.log(tx_hash); + + } catch (error) { + console.error(error); + } + +} + +main(); diff --git a/examples/Javascript/js-example/2_transfer.js b/examples/Javascript/js-example/2_transfer.js new file mode 100644 index 00000000..e8a67e11 --- /dev/null +++ b/examples/Javascript/js-example/2_transfer.js @@ -0,0 +1,35 @@ +import init, * as wasm from "./web-dist/zklink-sdk-web.js"; + +async function main() { + await init(); + const private_key = "be725250b123a39dab5b7579334d5888987c72a58f4508062545fe6e08ca94f4"; + const eth_signer = wasm.EthPrivateKeySigner.newFromHexPrivateKey(private_key); + const zklink_signer = wasm.ZklinkSigner.newFromEthSigner(private_key); + const to_address = "0x5505a8cD4594Dbf79d8C59C0Df1414AB871CA896"; + const ts = Math.floor(Date.now() / 1000); + let tx = new wasm.Transfer(10, to_address, 1, 1, 18, "100000000000000", "10000000000000000", 1,ts); + let message = tx.getEthSignMessage("USDT"); + const msg = new TextEncoder().encode(message); + try { + let signature = eth_signer.signMessage(msg); + console.log(signature); + let tx_signature = tx.sign(zklink_signer); + console.log(tx_signature); + tx.setL2Signature(tx_signature); + //send to zklink + let provder = new wasm.Provider("testnet"); + let signed_tx = new wasm.SignedTransaction(tx.getTxType(),tx.getTx()); + console.log(signed_tx); + let l1_signature = new wasm.TxL1Signature(wasm.L1SignatureType.Eth,signature); + let submitter_signature = tx.submitterSign(zklink_signer); + console.log(submitter_signature); + let tx_hash = await provder.sendTransaction(signed_tx,l1_signature,submitter_signature); + console.log(tx_hash); + + } catch (error) { + console.error(error); + } + +} + +main(); diff --git a/examples/Javascript/js-example/index.html b/examples/Javascript/js-example/index.html index 81c90a09..10de3d41 100644 --- a/examples/Javascript/js-example/index.html +++ b/examples/Javascript/js-example/index.html @@ -6,7 +6,7 @@ - \ No newline at end of file diff --git a/provider/Cargo.toml b/provider/Cargo.toml index 481c0658..78ea8ef3 100644 --- a/provider/Cargo.toml +++ b/provider/Cargo.toml @@ -6,15 +6,25 @@ edition = "2021" [dependencies] bigdecimal = { version = "0.3.0", features = ["serde"] } -chrono = { version = "0.4", features = ["serde", "rustc-serialize"] } -jsonrpsee = { version = "0.15.1", features = ["server", "client", "macros"] } +chrono = { version = "0.4", features = ["serde"] } +reqwest = { version = "0.11", default-features = false, features = ["blocking", "json", "rustls-tls"] } serde = "1.0.137" +serde_json = "1.0" +thiserror = "1.0" +wasm-bindgen = { version = "0.2.87", features = ["serde-serialize"] } zklink_sdk_signers = { path = "../signers" } zklink_sdk_types = { path = "../types" } +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +jsonrpsee = { version = "0.20.1", features = ["http-client","macros"] } + +[target.'cfg(target_arch = "wasm32")'.dependencies] +jsonrpsee = { version = "0.20.1", features = ["macros","jsonrpsee-types","client-core"] } + [dev-dependencies] serde_json = "1.0" tokio = { version = "1", features = ["full"] } +wasm-bindgen-test = "0.3" [features] default = [] diff --git a/provider/src/error.rs b/provider/src/error.rs new file mode 100644 index 00000000..19a79a97 --- /dev/null +++ b/provider/src/error.rs @@ -0,0 +1,16 @@ +use jsonrpsee::core::error::Error as jsonrpseeError; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum RpcError { + #[error("Invalid network")] + InvalidNetwork, + #[error("Parse params error: {0}")] + ParseParamsError(jsonrpseeError), + #[error("HTTP request error: {0}")] + RequestError(reqwest::Error), + #[error("Parse response error: {0}")] + ResponseError(reqwest::Error), + #[error("Parse json value error")] + ParseJsonError, +} diff --git a/provider/src/lib.rs b/provider/src/lib.rs index bd9eee94..ef57f599 100644 --- a/provider/src/lib.rs +++ b/provider/src/lib.rs @@ -1,9 +1,14 @@ +pub mod error; pub mod network; pub mod response; #[cfg(not(feature = "ffi"))] +#[cfg(not(target_arch = "wasm32"))] pub mod rpc; +#[cfg(target_arch = "wasm32")] +pub mod rpc_wasm; #[cfg(not(feature = "ffi"))] +#[cfg(not(target_arch = "wasm32"))] mod not_ffi { use crate::network::Network; use jsonrpsee::http_client::{HttpClient, HttpClientBuilder}; @@ -36,8 +41,11 @@ mod not_ffi { } #[cfg(not(feature = "ffi"))] +#[cfg(not(target_arch = "wasm32"))] pub use crate::rpc::ZkLinkRpcClient; #[cfg(not(feature = "ffi"))] +#[cfg(not(target_arch = "wasm32"))] pub use jsonrpsee::core::Error as RpcError; #[cfg(not(feature = "ffi"))] +#[cfg(not(target_arch = "wasm32"))] pub use not_ffi::*; diff --git a/provider/src/network.rs b/provider/src/network.rs index f8a49752..e6d6ac62 100644 --- a/provider/src/network.rs +++ b/provider/src/network.rs @@ -1,4 +1,6 @@ +use crate::error::RpcError; use serde::{Deserialize, Serialize}; +use std::str::FromStr; /// Network to be used for a zklink client. #[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)] @@ -18,3 +20,15 @@ impl Network { } } } + +impl FromStr for Network { + type Err = RpcError; + + fn from_str(s: &str) -> Result { + match s { + "mainet" => Ok(Network::MainNet), + "testnet" => Ok(Network::TestNet), + _ => Err(RpcError::InvalidNetwork), + } + } +} diff --git a/provider/src/rpc_wasm.rs b/provider/src/rpc_wasm.rs new file mode 100644 index 00000000..0f05d66b --- /dev/null +++ b/provider/src/rpc_wasm.rs @@ -0,0 +1,262 @@ +use crate::error::RpcError; +use crate::response::{AccountQuery, AccountSnapshotResp, TokenResp}; +use jsonrpsee::core::params::ArrayParams; +use jsonrpsee::core::traits::ToRpcParams; +use jsonrpsee::types::request::Request; +use jsonrpsee::types::Id; +use std::collections::HashMap; +use wasm_bindgen::JsValue; +use zklink_sdk_signers::zklink_signer::ZkLinkSignature; +use zklink_sdk_types::basic_types::{BlockNumber, SubAccountId, TokenId}; +use zklink_sdk_types::prelude::TxHash; +use zklink_sdk_types::signatures::TxLayer1Signature; +use zklink_sdk_types::tx_type::zklink_tx::ZkLinkTx; + +impl From for JsValue { + fn from(error: RpcError) -> Self { + JsValue::from_str(&format!("error: {error}")) + } +} + +pub struct WasmRpcClient { + pub server_url: String, +} + +impl WasmRpcClient { + pub fn new(server_url: String) -> Self { + Self { server_url } + } + + pub async fn tokens(&self) -> Result, RpcError> { + let request = Request::new("getSupportTokens".into(), None, Id::Number(1)); + let res = reqwest::Client::new() + .post(&self.server_url) + .json(&request) + .send() + .await + .map_err(RpcError::RequestError)? + .json::>() + .await + .map_err(RpcError::ResponseError)?; + if let Some(&ref result) = res.get("result") { + let resp: HashMap = + serde_json::from_value(result.clone()).map_err(|_e| RpcError::ParseJsonError)?; + Ok(resp) + } else { + Err(RpcError::ParseJsonError) + } + } + + pub async fn account_query( + &self, + account_query: AccountQuery, + sub_account_id: Option, + block_number: Option, + ) -> Result { + let mut builder = ArrayParams::new(); + let _ = builder.insert(account_query); + let _ = builder.insert(sub_account_id); + let _ = builder.insert(block_number); + let params = builder + .to_rpc_params() + .map_err(RpcError::ParseParamsError)?; + let request = Request::new( + "getAccountSnapshot".into(), + params.as_ref().map(|p| p.as_ref()), + Id::Number(1), + ); + let res = reqwest::Client::new() + .post(&self.server_url) + .json(&request) + .send() + .await + .map_err(RpcError::RequestError)? + .json::>() + .await + .map_err(RpcError::ResponseError)?; + if let Some(&ref result) = res.get("result") { + let resp: AccountSnapshotResp = + serde_json::from_value(result.clone()).map_err(|_e| RpcError::ParseJsonError)?; + Ok(resp) + } else { + Err(RpcError::ParseJsonError) + } + } + + pub async fn send_transaction( + &self, + tx: ZkLinkTx, + eth_signature: Option, + submitter_signature: Option, + ) -> Result { + let mut builder = ArrayParams::new(); + let _ = builder.insert(tx); + let _ = builder.insert(eth_signature); + let _ = builder.insert(submitter_signature); + let params = builder + .to_rpc_params() + .map_err(RpcError::ParseParamsError)?; + let request = Request::new( + "sendTransaction".into(), + params.as_ref().map(|p| p.as_ref()), + Id::Number(0), + ); + let res = reqwest::Client::new() + .post(&self.server_url) + .json(&request) + .send() + .await + .map_err(RpcError::RequestError)? + .json::>() + .await + .map_err(RpcError::ResponseError)?; + if let Some(&ref result) = res.get("result") { + let resp: TxHash = + serde_json::from_value(result.clone()).map_err(|_e| RpcError::ParseJsonError)?; + Ok(resp) + } else { + Err(RpcError::ParseJsonError) + } + } +} + +#[cfg(test)] +mod test { + use super::WasmRpcClient; + use crate::network::Network; + use crate::rpc::ZkLinkRpcClient; + use crate::ZkLinkRpcProvider; + use std::str::FromStr; + use std::sync::Arc; + use std::time::Duration; + use zklink_sdk_signers::eth_signer::EthSigner; + use zklink_sdk_signers::zklink_signer::{PubKeyHash, ZkLinkSigner}; + use zklink_sdk_types::basic_types::BigUint; + use zklink_sdk_types::basic_types::{ + AccountId, ChainId, Nonce, SubAccountId, TimeStamp, TokenId, ZkLinkAddress, + }; + use zklink_sdk_types::signatures::TxLayer1Signature; + use zklink_sdk_types::tx_builder::{ChangePubKeyBuilder, TransferBuilder}; + use zklink_sdk_types::tx_type::change_pubkey::ChangePubKey; + use zklink_sdk_types::tx_type::transfer::Transfer; + use zklink_sdk_types::tx_type::zklink_tx::ZkLinkTx; + use zklink_sdk_types::tx_type::ZkSignatureTrait; + + #[tokio::test] + async fn test_tokens() { + let client = WasmRpcClient::new("https://api-v1.zk.link".to_owned()); + let ret = client.tokens().await.unwrap(); + println!("{:?}", ret); + } + + #[tokio::test] + async fn test_send_change_pubkey() { + let private_key = "be725250b123a39dab5b7579334d5888987c72a58f4508062545fe6e08ca94f4"; + let eth_signer = EthSigner::try_from(private_key).unwrap(); + let zklink_signer = ZkLinkSigner::new_from_hex_eth_signer(private_key).unwrap(); + let main_contract = "0x5505a8cD4594Dbf79d8C59C0Df1414AB871CA896"; + let l1_client_id = 80001; + let new_pubkey_hash = "0xd8d5fb6a6caef06aa3dc2abdcdc240987e5330fe"; + let ts = 1696595303; + //auth type 'ECDSA' + let builder = ChangePubKeyBuilder { + chain_id: ChainId(1), + account_id: AccountId(10), + sub_account_id: SubAccountId(1), + new_pubkey_hash: PubKeyHash::from_hex(new_pubkey_hash).unwrap(), + fee_token: TokenId(18), + fee: BigUint::from(100000000000000u64), + nonce: Nonce(1), + eth_signature: None, + timestamp: TimeStamp(ts), + }; + let mut change_pubkey = ChangePubKey::new(builder); + let message = change_pubkey + .to_eip712_request_payload( + l1_client_id, + &ZkLinkAddress::from_str(&main_contract).unwrap(), + ) + .unwrap(); + let signature = eth_signer + .sign_message(message.raw_data.as_bytes()) + .unwrap(); + let builder_with_sig = ChangePubKeyBuilder { + chain_id: ChainId(1), + account_id: AccountId(10), + sub_account_id: SubAccountId(1), + new_pubkey_hash: PubKeyHash::from_hex(new_pubkey_hash).unwrap(), + fee_token: TokenId(18), + fee: BigUint::from(100000000000000u64), + nonce: Nonce(1), + eth_signature: Some(signature), + timestamp: TimeStamp(ts), + }; + let mut tx = ChangePubKey::new(builder_with_sig); + tx.sign(&zklink_signer).unwrap(); + + // for wasm + // let client = WasmRpcClient::new("https://aws-gw-v2.zk.link".to_owned()); + // client.send_transaction(ZkLinkTx::ChangePubKey(Box::new(tx.clone())),None,Some(tx.signature.clone())).await.unwrap(); + + //use jsonrpsee + let client = ZkLinkRpcProvider::new(Network::TestNet, Duration::from_secs(3)); + let ret = client + .tx_submit( + ZkLinkTx::ChangePubKey(Box::new(tx.clone())), + None, + Some(tx.signature.clone()), + ) + .await + .unwrap(); + } + + #[tokio::test] + async fn test_send_transfer() { + let private_key = "be725250b123a39dab5b7579334d5888987c72a58f4508062545fe6e08ca94f4"; + let eth_signer = EthSigner::try_from(private_key).unwrap(); + let zklink_signer = ZkLinkSigner::new_from_hex_eth_signer(private_key).unwrap(); + let to_address = "0x5505a8cD4594Dbf79d8C59C0Df1414AB871CA896"; + let l1_client_id = 80001; + let ts = 1696595303; + //auth type 'ECDSA' + let builder = TransferBuilder { + account_id: AccountId(10), + to_address: ZkLinkAddress::from_str(to_address).unwrap(), + from_sub_account_id: SubAccountId(1), + to_sub_account_id: SubAccountId(1), + token: TokenId(18), + fee: BigUint::from(100000000000000u64), + nonce: Nonce(1), + timestamp: TimeStamp(ts), + amount: BigUint::from(1000000000000000u64), + }; + let mut transfer = Transfer::new(builder); + let eth_signature = eth_signer + .sign_message(transfer.get_eth_sign_msg("USDT").as_bytes()) + .unwrap(); + transfer.sign(&zklink_signer).unwrap(); + + let submiiter_signature = transfer + .submitter_signature(Arc::new(zklink_signer)) + .unwrap(); + // for wasm + let client = WasmRpcClient::new("https://aws-gw-v2.zk.link".to_owned()); + client + .send_transaction( + ZkLinkTx::Transfer(Box::new(transfer.clone())), + Some(TxLayer1Signature::EthereumSignature(eth_signature)), + Some(submiiter_signature), + ) + .await + .unwrap(); + + //use jsonrpsee + // let client = ZkLinkRpcProvider::new(Network::TestNet, Duration::from_secs(3)); + // let ret = client.tx_submit( + // ZkLinkTx::Transfer(Box::new(transfer.clone())), + // None, + // Some(transfer.signature.clone()) + // ) + // .await.unwrap(); + } +} diff --git a/signers/Cargo.toml b/signers/Cargo.toml index 5c2809c0..6ac4f1cf 100644 --- a/signers/Cargo.toml +++ b/signers/Cargo.toml @@ -24,7 +24,5 @@ default = [] ffi = [] [dev-dependencies] -actix-rt = "2.7" -actix-web = "4.2" futures = "0.3" tokio = { version = "1", features = ["full"] } diff --git a/signers/src/eth_signer/eip1271_signature.rs b/signers/src/eth_signer/eip1271_signature.rs index 3460e0f7..9938dba7 100644 --- a/signers/src/eth_signer/eip1271_signature.rs +++ b/signers/src/eth_signer/eip1271_signature.rs @@ -3,7 +3,7 @@ use std::fmt; use zklink_sdk_utils::serde::ZeroPrefixHexSerde; #[derive(Debug, Clone, PartialEq, Eq)] -pub struct EIP1271Signature(pub(crate) Vec); +pub struct EIP1271Signature(pub Vec); impl AsRef<[u8]> for EIP1271Signature { fn as_ref(&self) -> &[u8] { diff --git a/signers/src/eth_signer/mod.rs b/signers/src/eth_signer/mod.rs index 3ba04b30..2acee1a5 100644 --- a/signers/src/eth_signer/mod.rs +++ b/signers/src/eth_signer/mod.rs @@ -13,8 +13,9 @@ pub use error::EthSignerError; pub use ethers_primitives::Address as EIP712Address; pub use packed_eth_signature::PackedEthSignature; pub use pk_signer::EthSigner; +use serde::{Deserialize, Serialize}; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct EthTypedData { pub raw_data: String, pub data_hash: H256, diff --git a/types/src/tx_type/mod.rs b/types/src/tx_type/mod.rs index 96d6b821..ac60c677 100644 --- a/types/src/tx_type/mod.rs +++ b/types/src/tx_type/mod.rs @@ -155,4 +155,11 @@ pub trait ZkSignatureTrait: TxTrait { let signature = signer.sign_musig(&bytes)?; Ok(signature) } + + #[cfg(not(feature = "ffi"))] + fn submitter_signature(&self, signer: &ZkLinkSigner) -> Result { + let bytes = self.tx_hash(); + let signature = signer.sign_musig(&bytes)?; + Ok(signature) + } } From 8cdc06c1dd4d2a002bfe7e432f89fffccba4e6df Mon Sep 17 00:00:00 2001 From: nickwest Date: Sun, 8 Oct 2023 02:18:13 -0700 Subject: [PATCH 2/6] impl all rpc method in wasm --- bindings/wasm/src/error.rs | 55 ---- bindings/wasm/src/provider.rs | 180 +++++++++++- bindings/wasm/src/rpc.rs | 28 +- examples/Javascript/js-example/index.html | 3 +- examples/Javascript/js-example/index.js | 17 -- examples/Javascript/js-example/rpc.js | 62 ++++ provider/Cargo.toml | 2 + provider/src/error.rs | 2 + provider/src/response.rs | 1 + provider/src/rpc_wasm.rs | 327 +++++++++++++++++----- 10 files changed, 535 insertions(+), 142 deletions(-) delete mode 100644 bindings/wasm/src/error.rs delete mode 100644 examples/Javascript/js-example/index.js create mode 100644 examples/Javascript/js-example/rpc.js diff --git a/bindings/wasm/src/error.rs b/bindings/wasm/src/error.rs deleted file mode 100644 index 310fed7c..00000000 --- a/bindings/wasm/src/error.rs +++ /dev/null @@ -1,55 +0,0 @@ -use thiserror::Error; -use zklink_sdk_provider::RpcError; -use zklink_sdk_signers::eth_signer::error::EthSignerError; -use zklink_sdk_signers::zklink_signer::error::ZkSignerError; -use zklink_sdk_types::basic_types::ChainId; -use wasm_bindgen::JsValue; - -#[derive(Debug, Error)] -pub enum ClientError { - #[error("Network '{0}' is not supported")] - NetworkNotSupported(ChainId), - #[error("Unable to decode server response: {0}")] - MalformedResponse(String), - #[error("RPC error: {0:?}")] - RpcError(#[from] RpcError), - #[error("Network error: {0}")] - NetworkError(String), - - #[error("Provided account credentials are incorrect")] - IncorrectCredentials, - #[error("Seed too short, must be at least 32 bytes long")] - SeedTooShort, - #[error("Token is not supported by zkLink")] - UnknownToken, - #[error("Incorrect address")] - IncorrectAddress, - - #[error("Operation timeout")] - OperationTimeout, - #[error("Polling interval is too small")] - PollingIntervalIsTooSmall, - - #[error("EthSigning error: {0}")] - EthSigningError(#[from] EthSignerError), - #[error("ZkSigning error: {0}")] - ZkSigningError(#[from] ZkSignerError), - #[error("Missing required field for a transaction: {0}")] - MissingRequiredField(String), - - #[error("Ethereum private key was not provided for this wallet")] - NoEthereumPrivateKey, - - #[error("Provided value is not packable")] - NotPackableValue, - #[error("Non-zero subAccountId required submitter signer")] - MissSubmitterSigner, - #[error("Incorrect tx format")] - IncorrectTx, -} - -impl From for JsValue { - fn from(error: ClientError) -> Self { - JsValue::from_str(&format!("error: {error}")) - } -} diff --git a/bindings/wasm/src/provider.rs b/bindings/wasm/src/provider.rs index b69657fa..97712845 100644 --- a/bindings/wasm/src/provider.rs +++ b/bindings/wasm/src/provider.rs @@ -1,14 +1,21 @@ -use crate::rpc::{AccountQueryParam, SignedTransaction, TxL1Signature}; +use crate::rpc::{AccountQueryParam, L2TxType, SignedTransaction, TxL1Signature}; use std::collections::HashMap; use std::str::FromStr; use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::JsValue; +use zklink_sdk_provider::error::RpcError; use zklink_sdk_provider::network::Network; -use zklink_sdk_provider::response::{AccountSnapshotResp, TokenResp}; +use zklink_sdk_provider::response::{ + AccountInfoResp, AccountSnapshotResp, BlockNumberResp, BlockOnChainResp, BlockResp, ChainResp, + FastWithdrawTxResp, ForwardTxResp, Page, SubAccountBalances, SubAccountOrders, TokenResp, + TxHashOrDetailResp, TxResp, ZkLinkTxHistory, +}; use zklink_sdk_provider::rpc_wasm::WasmRpcClient; use zklink_sdk_signers::zklink_signer::ZkLinkSignature; +use zklink_sdk_types::basic_types::bigunit_wrapper::BigUintSerdeWrapper; use zklink_sdk_types::basic_types::tx_hash::TxHash; -use zklink_sdk_types::basic_types::{BlockNumber, SubAccountId, TokenId}; +use zklink_sdk_types::basic_types::{AccountId, BlockNumber, ChainId, SubAccountId, TokenId}; +use zklink_sdk_types::prelude::ZkLinkAddress; #[wasm_bindgen] pub struct Provider { @@ -26,13 +33,13 @@ impl Provider { } } - #[wasm_bindgen] + #[wasm_bindgen(js_name=getSupportTokens)] pub async fn tokens(&self) -> Result { let result: HashMap = self.client.tokens().await?; Ok(serde_wasm_bindgen::to_value(&result)?) } - #[wasm_bindgen(js_name=accountQuery)] + #[wasm_bindgen(js_name=getAccountSnapshot)] pub async fn account_query( &self, account_query: AccountQueryParam, @@ -67,4 +74,167 @@ impl Provider { .await?; Ok(result.as_hex()) } + + #[wasm_bindgen(js_name=getSupportChains)] + pub async fn get_support_chains(&self) -> Result { + let result: Vec = self.client.get_support_chains().await?; + Ok(serde_wasm_bindgen::to_value(&result)?) + } + + #[wasm_bindgen(js_name=getLatestBlockNumber)] + pub async fn block_info(&self) -> Result { + let result: BlockNumberResp = self.client.block_info().await?; + Ok(serde_wasm_bindgen::to_value(&result)?) + } + + #[wasm_bindgen(js_name=getBlockByNumber)] + pub async fn block_detail( + &self, + block_number: Option, + include_tx: bool, + include_update: bool, + ) -> Result { + let result: BlockResp = self + .client + .block_detail( + block_number.map(|b| BlockNumber(b)), + include_tx, + include_update, + ) + .await?; + Ok(serde_wasm_bindgen::to_value(&result)?) + } + + #[wasm_bindgen(js_name=getPendingBlock)] + pub async fn pending_block_detail( + &self, + last_tx_timestamp_micro: u64, + include_tx: bool, + include_update: bool, + limit: Option, + ) -> Result { + let result: Vec = self + .client + .pending_block_detail(last_tx_timestamp_micro, include_tx, include_update, limit) + .await?; + Ok(serde_wasm_bindgen::to_value(&result)?) + } + + #[wasm_bindgen(js_name=getBlockOnChainByNumber)] + pub async fn block_onchain_detail(&self, block_number: u32) -> Result { + let result: BlockOnChainResp = self + .client + .block_onchain_detail(BlockNumber(block_number)) + .await?; + Ok(serde_wasm_bindgen::to_value(&result)?) + } + + #[wasm_bindgen(js_name=getAccount)] + pub async fn account_info(&self, account_query: AccountQueryParam) -> Result { + let result: AccountInfoResp = self.client.account_info(account_query.into()).await?; + Ok(serde_wasm_bindgen::to_value(&result)?) + } + + #[wasm_bindgen(js_name=getAccountBalances)] + pub async fn account_balances( + &self, + account_id: u32, + sub_account_id: Option, + ) -> Result { + let result: SubAccountBalances = self + .client + .account_balances( + AccountId(account_id), + sub_account_id.map(|id| SubAccountId(id)), + ) + .await?; + Ok(serde_wasm_bindgen::to_value(&result)?) + } + + #[wasm_bindgen(js_name=getAccountOrderSlots)] + pub async fn account_order_slots( + &self, + account_id: u32, + sub_account_id: Option, + ) -> Result { + let result: SubAccountOrders = self + .client + .account_order_slots( + AccountId(account_id), + sub_account_id.map(|id| SubAccountId(id)), + ) + .await?; + Ok(serde_wasm_bindgen::to_value(&result)?) + } + + #[wasm_bindgen(js_name=getTokenReserve)] + pub async fn token_remain(&self, token_id: u32, mapping: bool) -> Result { + let result: HashMap = + self.client.token_remain(TokenId(token_id), mapping).await?; + Ok(serde_wasm_bindgen::to_value(&result)?) + } + + #[wasm_bindgen(js_name=getTransactionByHash)] + pub async fn tx_info(&self, hash: String, include_update: bool) -> Result { + let hash = TxHash::from_hex(&hash).map_err(|_e| RpcError::InvalidInputParameter)?; + let result: TxResp = self.client.tx_info(hash, include_update).await?; + Ok(serde_wasm_bindgen::to_value(&result)?) + } + + #[wasm_bindgen(js_name=getAccountTransactionHistory)] + pub async fn tx_history( + &self, + tx_type: L2TxType, + address: String, + page_index: u64, + page_size: u32, + ) -> Result { + let address = + ZkLinkAddress::from_hex(&address).map_err(|_e| RpcError::InvalidInputParameter)?; + let result: Page = self + .client + .tx_history(tx_type.into(), address, page_index, page_size) + .await?; + Ok(serde_wasm_bindgen::to_value(&result)?) + } + + #[wasm_bindgen(js_name=getFastWithdrawTxs)] + pub async fn tx_fast_withdraw( + &self, + last_tx_timestamp: u64, + max_txs: u32, + ) -> Result { + let result: Vec = self + .client + .tx_fast_withdraw(last_tx_timestamp, max_txs) + .await?; + Ok(serde_wasm_bindgen::to_value(&result)?) + } + + #[wasm_bindgen(js_name=pullForwardTxs)] + pub async fn pull_forward_txs( + &self, + sub_account_id: u8, + offset_id: i64, + limit: i64, + ) -> Result { + let result: Vec = self + .client + .pull_forward_txs(SubAccountId(sub_account_id), offset_id, limit) + .await?; + Ok(serde_wasm_bindgen::to_value(&result)?) + } + + #[wasm_bindgen(js_name=confirmFullExit)] + pub async fn confirm_full_exit( + &self, + tx_hash: String, + submitter_signature: String, + ) -> Result { + let hash = TxHash::from_hex(&tx_hash).map_err(|_e| RpcError::InvalidInputParameter)?; + let signature = ZkLinkSignature::from_hex(&submitter_signature) + .map_err(|_e| RpcError::InvalidInputParameter)?; + let result: bool = self.client.confirm_full_exit(hash, signature).await?; + Ok(result) + } } diff --git a/bindings/wasm/src/rpc.rs b/bindings/wasm/src/rpc.rs index 92826c5f..a170eb4f 100644 --- a/bindings/wasm/src/rpc.rs +++ b/bindings/wasm/src/rpc.rs @@ -9,7 +9,7 @@ use zklink_sdk_types::prelude::ZkLinkAddress; use zklink_sdk_types::signatures::TxLayer1Signature; use zklink_sdk_types::tx_type::change_pubkey::ChangePubKey; use zklink_sdk_types::tx_type::transfer::Transfer; -use zklink_sdk_types::tx_type::zklink_tx::ZkLinkTx; +use zklink_sdk_types::tx_type::zklink_tx::{ZkLinkTx, ZkLinkTxType}; #[wasm_bindgen] #[derive(Copy, Clone)] @@ -44,6 +44,17 @@ pub struct SignedTransaction { tx: JsValue, } +#[wasm_bindgen] +pub enum L2TxType { + Deposit, + FullExit, + ChangePubKey, + Transfer, + Withdraw, + ForcedExit, + OrderMatching, +} + #[wasm_bindgen] impl AccountQueryParam { #[wasm_bindgen(constructor)] @@ -102,6 +113,7 @@ impl SignedTransaction { SignedTransaction { tx_type, tx } } } + impl From for ZkLinkTx { fn from(tx: SignedTransaction) -> ZkLinkTx { match tx.tx_type { @@ -119,3 +131,17 @@ impl From for ZkLinkTx { } } } + +impl From for ZkLinkTxType { + fn from(tx_type: L2TxType) -> ZkLinkTxType { + match tx_type { + L2TxType::Deposit => ZkLinkTxType::Deposit, + L2TxType::Transfer => ZkLinkTxType::Transfer, + L2TxType::ChangePubKey => ZkLinkTxType::ChangePubKey, + L2TxType::OrderMatching => ZkLinkTxType::OrderMatching, + L2TxType::FullExit => ZkLinkTxType::FullExit, + L2TxType::ForcedExit => ZkLinkTxType::ForcedExit, + L2TxType::Withdraw => ZkLinkTxType::Withdraw, + } + } +} diff --git a/examples/Javascript/js-example/index.html b/examples/Javascript/js-example/index.html index 10de3d41..44271a6f 100644 --- a/examples/Javascript/js-example/index.html +++ b/examples/Javascript/js-example/index.html @@ -6,7 +6,8 @@ - \ No newline at end of file diff --git a/examples/Javascript/js-example/index.js b/examples/Javascript/js-example/index.js deleted file mode 100644 index 06167478..00000000 --- a/examples/Javascript/js-example/index.js +++ /dev/null @@ -1,17 +0,0 @@ -import init, { ZklinkSignerWasm } from "./web-dist/zklink-sdk-web.js"; - -async function main() { - await init(); - const signer = ZklinkSignerWasm.NewFromEthSigner("be725250b123a39dab5b7579334d5888987c72a58f4508062545fe6e08ca94f4"); - const msg_str = "hello world!"; - const msg = new TextEncoder().encode(msg_str); - try { - let signature = signer.sign(msg); - console.log(signature); - } catch (error) { - console.error(error); - } - -} - -main(); diff --git a/examples/Javascript/js-example/rpc.js b/examples/Javascript/js-example/rpc.js new file mode 100644 index 00000000..f435d8e3 --- /dev/null +++ b/examples/Javascript/js-example/rpc.js @@ -0,0 +1,62 @@ +import init, * as wasm from "./web-dist/zklink-sdk-web.js"; +async function main() { + await init(); + try { + let provider = new wasm.Provider("mainet"); + // 1.getSupportTokens + let tokens = await provider.getSupportTokens(); + console.log(tokens); + // 2.getAccountSnapshot + let account_id = new wasm.AccountQueryParam(wasm.AccountQueryType.AccountId, "5"); + let sub_account_id = 1; + // let block_number = 100; + let account_resp = await provider.getAccountSnapshot(account_id,sub_account_id,null); + console.log(account_resp); + // 3.sendTransaction(test on the tx example) + // 4.getSupportChains + let chains = await provider.getSupportChains(); + console.log(chains); + // 5.getLatestBlockNumber + let block_info = await provider.getLatestBlockNumber(); + console.log(block_info); + // 6.getBlockByNumber + let block_detail = await provider.getBlockByNumber(100,true,true); + console.log(block_detail); + // 7.getPendingBlock + let pending_block_info = await provider.getPendingBlock(1696743981000n,true,true,null); + console.log(pending_block_info); + // 8.getBlockOnChainByNumber + let on_chain_block_info = await provider.getBlockOnChainByNumber(100); + console.log(on_chain_block_info); + // 9.getAccount + let get_account_id = new wasm.AccountQueryParam(wasm.AccountQueryType.AccountId, "10"); + let account = await provider.getAccount(get_account_id); + console.log(account); + // 10.getAccountBalances + let balances = await provider.getAccountBalances(20,1); + console.log(balances); + // 11.getAccountOrderSlots + let slots = await provider.getAccountOrderSlots(20,1); + console.log(slots); + // 12.getTokenReserve + let reserve = await provider.getTokenReserve(18,false); + console.log(reserve); + // 13.getTransactionByHash + let tx_hash = "0x0cbeabac1a2257fb095c2465e148570e32793345442b39bf64cad4ed87475f9b"; + let tx_info = await provider.getTransactionByHash(tx_hash,false); + console.log(tx_info); + // 14.getAccountTransactionHistory + let history = await provider.getAccountTransactionHistory(wasm.L2TxType.Deposit,"0x12aFF993702B5d623977A9044686Fa1A2B0c2147",0n,5); + console.log(history); + // 15.getFastWithdrawTxs + let fast_withdraw_txs = await provider.getFastWithdrawTxs(1696743981000n,10); + console.log(fast_withdraw_txs); + // 16.pullForwardTxs + // 17.confirmFullExit + } catch (error) { + console.error(error); + } + +} + +main(); diff --git a/provider/Cargo.toml b/provider/Cargo.toml index 78ea8ef3..af31f504 100644 --- a/provider/Cargo.toml +++ b/provider/Cargo.toml @@ -20,6 +20,8 @@ jsonrpsee = { version = "0.20.1", features = ["http-client","macros"] } [target.'cfg(target_arch = "wasm32")'.dependencies] jsonrpsee = { version = "0.20.1", features = ["macros","jsonrpsee-types","client-core"] } +getrandom = { version = "0.2.10", features = ["js"] } +uuid = "0.8" [dev-dependencies] serde_json = "1.0" diff --git a/provider/src/error.rs b/provider/src/error.rs index 19a79a97..80f33d75 100644 --- a/provider/src/error.rs +++ b/provider/src/error.rs @@ -5,6 +5,8 @@ use thiserror::Error; pub enum RpcError { #[error("Invalid network")] InvalidNetwork, + #[error("Invalid input parameter")] + InvalidInputParameter, #[error("Parse params error: {0}")] ParseParamsError(jsonrpseeError), #[error("HTTP request error: {0}")] diff --git a/provider/src/response.rs b/provider/src/response.rs index a339ca32..49706a46 100644 --- a/provider/src/response.rs +++ b/provider/src/response.rs @@ -279,5 +279,6 @@ pub struct ZkLinkTxHistory { pub tx: ZkLinkTx, pub tx_hash: TxHash, pub tx_receipt: TxReceiptResp, + #[serde(with = "ts_microseconds")] pub created_at: DateTime, } diff --git a/provider/src/rpc_wasm.rs b/provider/src/rpc_wasm.rs index 0f05d66b..9a3de3e8 100644 --- a/provider/src/rpc_wasm.rs +++ b/provider/src/rpc_wasm.rs @@ -1,5 +1,10 @@ use crate::error::RpcError; -use crate::response::{AccountQuery, AccountSnapshotResp, TokenResp}; +use crate::response::{ + AccountInfoResp, AccountQuery, AccountSnapshotResp, BlockNumberResp, BlockOnChainResp, + BlockResp, ChainResp, FastWithdrawTxResp, ForwardTxResp, Page, SubAccountBalances, + SubAccountOrders, TokenResp, TxHashOrDetailResp, TxResp, ZkLinkTxHistory, +}; +use getrandom::getrandom; use jsonrpsee::core::params::ArrayParams; use jsonrpsee::core::traits::ToRpcParams; use jsonrpsee::types::request::Request; @@ -7,10 +12,13 @@ use jsonrpsee::types::Id; use std::collections::HashMap; use wasm_bindgen::JsValue; use zklink_sdk_signers::zklink_signer::ZkLinkSignature; -use zklink_sdk_types::basic_types::{BlockNumber, SubAccountId, TokenId}; +use zklink_sdk_types::basic_types::bigunit_wrapper::BigUintSerdeWrapper; +use zklink_sdk_types::basic_types::{ + AccountId, BlockNumber, ChainId, SubAccountId, TokenId, ZkLinkAddress, +}; use zklink_sdk_types::prelude::TxHash; use zklink_sdk_types::signatures::TxLayer1Signature; -use zklink_sdk_types::tx_type::zklink_tx::ZkLinkTx; +use zklink_sdk_types::tx_type::zklink_tx::{ZkLinkTx, ZkLinkTxType}; impl From for JsValue { fn from(error: RpcError) -> Self { @@ -18,19 +26,18 @@ impl From for JsValue { } } -pub struct WasmRpcClient { - pub server_url: String, -} - -impl WasmRpcClient { - pub fn new(server_url: String) -> Self { - Self { server_url } - } - - pub async fn tokens(&self) -> Result, RpcError> { - let request = Request::new("getSupportTokens".into(), None, Id::Number(1)); +macro_rules! make_rpc_request { + ($method:expr,$builder:expr, $server_url:expr, $resp_type: ty) => {{ + let params = $builder + .to_rpc_params() + .map_err(RpcError::ParseParamsError)?; + let request = Request::new( + $method.into(), + params.as_ref().map(|p| p.as_ref()), + Id::Str(uuid_str().into()), + ); let res = reqwest::Client::new() - .post(&self.server_url) + .post($server_url) .json(&request) .send() .await @@ -45,6 +52,33 @@ impl WasmRpcClient { } else { Err(RpcError::ParseJsonError) } + }}; +} + +pub fn uuid_str() -> String { + let mut bytes = [0; 16]; + getrandom(&mut bytes).expect("RNG failure!"); + + let uuid = uuid::Builder::from_bytes(bytes) + .set_variant(uuid::Variant::RFC4122) + .set_version(uuid::Version::Random) + .build(); + + uuid.to_string() +} + +pub struct WasmRpcClient { + pub server_url: String, +} + +impl WasmRpcClient { + pub fn new(server_url: String) -> Self { + Self { server_url } + } + + pub async fn tokens(&self) -> Result, RpcError> { + let builder = ArrayParams::new(); + make_rpc_request!("getSupportTokens",builder,&self.server_url,HashMap) } pub async fn account_query( @@ -57,30 +91,12 @@ impl WasmRpcClient { let _ = builder.insert(account_query); let _ = builder.insert(sub_account_id); let _ = builder.insert(block_number); - let params = builder - .to_rpc_params() - .map_err(RpcError::ParseParamsError)?; - let request = Request::new( - "getAccountSnapshot".into(), - params.as_ref().map(|p| p.as_ref()), - Id::Number(1), - ); - let res = reqwest::Client::new() - .post(&self.server_url) - .json(&request) - .send() - .await - .map_err(RpcError::RequestError)? - .json::>() - .await - .map_err(RpcError::ResponseError)?; - if let Some(&ref result) = res.get("result") { - let resp: AccountSnapshotResp = - serde_json::from_value(result.clone()).map_err(|_e| RpcError::ParseJsonError)?; - Ok(resp) - } else { - Err(RpcError::ParseJsonError) - } + make_rpc_request!( + "getAccountSnapshot", + builder, + &self.server_url, + AccountSnapshotResp + ) } pub async fn send_transaction( @@ -93,30 +109,198 @@ impl WasmRpcClient { let _ = builder.insert(tx); let _ = builder.insert(eth_signature); let _ = builder.insert(submitter_signature); - let params = builder - .to_rpc_params() - .map_err(RpcError::ParseParamsError)?; - let request = Request::new( - "sendTransaction".into(), - params.as_ref().map(|p| p.as_ref()), - Id::Number(0), - ); - let res = reqwest::Client::new() - .post(&self.server_url) - .json(&request) - .send() - .await - .map_err(RpcError::RequestError)? - .json::>() - .await - .map_err(RpcError::ResponseError)?; - if let Some(&ref result) = res.get("result") { - let resp: TxHash = - serde_json::from_value(result.clone()).map_err(|_e| RpcError::ParseJsonError)?; - Ok(resp) - } else { - Err(RpcError::ParseJsonError) - } + make_rpc_request!("sendTransaction", builder, &self.server_url, TxHash) + } + + pub async fn get_support_chains(&self) -> Result, RpcError> { + let builder = ArrayParams::new(); + make_rpc_request!( + "getSupportChains", + builder, + &self.server_url, + Vec + ) + } + + pub async fn block_info(&self) -> Result { + let builder = ArrayParams::new(); + make_rpc_request!( + "getLatestBlockNumber", + builder, + &self.server_url, + BlockNumberResp + ) + } + + pub async fn block_detail( + &self, + block_number: Option, + include_tx: bool, + include_update: bool, + ) -> Result { + let mut builder = ArrayParams::new(); + let _ = builder.insert(block_number); + let _ = builder.insert(include_tx); + let _ = builder.insert(include_update); + make_rpc_request!("getBlockByNumber", builder, &self.server_url, BlockResp) + } + + pub async fn pending_block_detail( + &self, + last_tx_timestamp_micro: u64, + include_tx: bool, + include_update: bool, + limit: Option, + ) -> Result, RpcError> { + let mut builder = ArrayParams::new(); + let _ = builder.insert(last_tx_timestamp_micro); + let _ = builder.insert(include_tx); + let _ = builder.insert(include_update); + let _ = builder.insert(limit); + make_rpc_request!( + "getPendingBlock", + builder, + &self.server_url, + Vec + ) + } + + pub async fn block_onchain_detail( + &self, + block_number: BlockNumber, + ) -> Result { + let mut builder = ArrayParams::new(); + let _ = builder.insert(block_number); + make_rpc_request!( + "getBlockOnChainByNumber", + builder, + &self.server_url, + BlockOnChainResp + ) + } + + pub async fn account_info( + &self, + account_query: AccountQuery, + ) -> Result { + let mut builder = ArrayParams::new(); + let _ = builder.insert(account_query); + make_rpc_request!("getAccount", builder, &self.server_url, AccountInfoResp) + } + + pub async fn account_balances( + &self, + account_id: AccountId, + sub_account_id: Option, + ) -> Result { + let mut builder = ArrayParams::new(); + let _ = builder.insert(account_id); + let _ = builder.insert(sub_account_id); + make_rpc_request!( + "getAccountBalances", + builder, + &self.server_url, + SubAccountBalances + ) + } + + pub async fn account_order_slots( + &self, + account_id: AccountId, + sub_account_id: Option, + ) -> Result { + let mut builder = ArrayParams::new(); + let _ = builder.insert(account_id); + let _ = builder.insert(sub_account_id); + make_rpc_request!( + "getAccountOrderSlots", + builder, + &self.server_url, + SubAccountOrders + ) + } + + pub async fn token_remain( + &self, + token_id: TokenId, + mapping: bool, + ) -> Result, RpcError> { + let mut builder = ArrayParams::new(); + let _ = builder.insert(token_id); + let _ = builder.insert(mapping); + make_rpc_request!("getTokenReserve",builder,&self.server_url,HashMap) + } + + pub async fn tx_info(&self, hash: TxHash, include_update: bool) -> Result { + let mut builder = ArrayParams::new(); + let _ = builder.insert(hash); + let _ = builder.insert(include_update); + make_rpc_request!("getTransactionByHash", builder, &self.server_url, TxResp) + } + + pub async fn tx_history( + &self, + tx_type: ZkLinkTxType, + address: ZkLinkAddress, + page_index: u64, + page_size: u32, + ) -> Result, RpcError> { + let mut builder = ArrayParams::new(); + let _ = builder.insert(tx_type); + let _ = builder.insert(address); + let _ = builder.insert(page_index); + let _ = builder.insert(page_size); + make_rpc_request!( + "getAccountTransactionHistory", + builder, + &self.server_url, + Page + ) + } + + pub async fn tx_fast_withdraw( + &self, + last_tx_timestamp: u64, + max_txs: u32, + ) -> Result, RpcError> { + let mut builder = ArrayParams::new(); + let _ = builder.insert(last_tx_timestamp); + let _ = builder.insert(max_txs); + make_rpc_request!( + "getFastWithdrawTxs", + builder, + &self.server_url, + Vec + ) + } + + pub async fn pull_forward_txs( + &self, + sub_account_id: SubAccountId, + offset_id: i64, + limit: i64, + ) -> Result, RpcError> { + let mut builder = ArrayParams::new(); + let _ = builder.insert(sub_account_id); + let _ = builder.insert(offset_id); + let _ = builder.insert(limit); + make_rpc_request!( + "pullForwardTxs", + builder, + &self.server_url, + Vec + ) + } + + pub async fn confirm_full_exit( + &self, + tx_hash: TxHash, + submitter_signature: ZkLinkSignature, + ) -> Result { + let mut builder = ArrayParams::new(); + let _ = builder.insert(tx_hash); + let _ = builder.insert(submitter_signature); + make_rpc_request!("confirmFullExit", builder, &self.server_url, bool) } } @@ -124,6 +308,7 @@ impl WasmRpcClient { mod test { use super::WasmRpcClient; use crate::network::Network; + use crate::response::ZkLinkTxHistory; use crate::rpc::ZkLinkRpcClient; use crate::ZkLinkRpcProvider; use std::str::FromStr; @@ -135,6 +320,7 @@ mod test { use zklink_sdk_types::basic_types::{ AccountId, ChainId, Nonce, SubAccountId, TimeStamp, TokenId, ZkLinkAddress, }; + use zklink_sdk_types::prelude::ZkLinkTxType; use zklink_sdk_types::signatures::TxLayer1Signature; use zklink_sdk_types::tx_builder::{ChangePubKeyBuilder, TransferBuilder}; use zklink_sdk_types::tx_type::change_pubkey::ChangePubKey; @@ -149,6 +335,21 @@ mod test { println!("{:?}", ret); } + #[tokio::test] + async fn test_tx_history() { + let client = WasmRpcClient::new("https://api-v1.zk.link".to_owned()); + let ret = client + .tx_history( + ZkLinkTxType::Deposit, + ZkLinkAddress::from_hex("0x12aFF993702B5d623977A9044686Fa1A2B0c2147").unwrap(), + 0, + 1, + ) + .await + .unwrap(); + println!("{:?}", ret); + } + #[tokio::test] async fn test_send_change_pubkey() { let private_key = "be725250b123a39dab5b7579334d5888987c72a58f4508062545fe6e08ca94f4"; From 931bc22dc9ac75f438c428d0240bab71522b63f0 Mon Sep 17 00:00:00 2001 From: nickwest Date: Mon, 9 Oct 2023 06:35:55 -0700 Subject: [PATCH 3/6] fix review suggests:merge the rpc_wasm to rpc_client in wasm --- bindings/wasm/Cargo.toml | 5 + bindings/wasm/src/lib.rs | 4 +- bindings/wasm/src/provider.rs | 240 --------- bindings/wasm/src/rpc_client.rs | 321 ++++++++++++ .../src/{rpc.rs => rpc_type_converter.rs} | 29 +- bindings/wasm/tests/test_provider.rs | 8 +- examples/Javascript/js-example/rpc.js | 99 ++-- provider/src/error.rs | 8 + provider/src/lib.rs | 2 - provider/src/rpc_wasm.rs | 463 ------------------ signers/src/eth_signer/pk_signer.rs | 2 + types/Cargo.toml | 1 + types/src/tx_type/zklink_tx.rs | 2 + 13 files changed, 397 insertions(+), 787 deletions(-) delete mode 100644 bindings/wasm/src/provider.rs create mode 100644 bindings/wasm/src/rpc_client.rs rename bindings/wasm/src/{rpc.rs => rpc_type_converter.rs} (81%) delete mode 100644 provider/src/rpc_wasm.rs diff --git a/bindings/wasm/Cargo.toml b/bindings/wasm/Cargo.toml index 0ef83b3d..664c1351 100644 --- a/bindings/wasm/Cargo.toml +++ b/bindings/wasm/Cargo.toml @@ -11,14 +11,19 @@ crate-type = ["cdylib","rlib"] zklink_sdk_signers = { path = "../../signers" } zklink_sdk_types = { path = "../../types" } zklink_sdk_provider = { path = "../../provider" } +serde_json = "1.0" wasm-bindgen = { version = "0.2.87",features = ["serde-serialize"] } serde-wasm-bindgen = "0.5" getrandom = { version = "0.2.10", features = ["js"] } web-sys = "0.3" hex = "0.4.3" +reqwest = { version = "0.11", default-features = false, features = ["blocking", "json", "rustls-tls"] } [target.'cfg(target_arch = "wasm32")'.dependencies] wasm-bindgen-futures = "0.4" +jsonrpsee = { version = "0.20.1", features = ["macros","jsonrpsee-types","client-core"] } +uuid = "0.8" + [features] default = [] ffi = [] diff --git a/bindings/wasm/src/lib.rs b/bindings/wasm/src/lib.rs index 1a366db5..047377a2 100644 --- a/bindings/wasm/src/lib.rs +++ b/bindings/wasm/src/lib.rs @@ -1,7 +1,7 @@ #![cfg(target_arch = "wasm32")] pub mod crypto; -pub mod provider; -pub mod rpc; +pub mod rpc_client; +pub mod rpc_type_converter; pub mod tx_types; // pub mod wallet; // pub mod error; diff --git a/bindings/wasm/src/provider.rs b/bindings/wasm/src/provider.rs deleted file mode 100644 index 97712845..00000000 --- a/bindings/wasm/src/provider.rs +++ /dev/null @@ -1,240 +0,0 @@ -use crate::rpc::{AccountQueryParam, L2TxType, SignedTransaction, TxL1Signature}; -use std::collections::HashMap; -use std::str::FromStr; -use wasm_bindgen::prelude::wasm_bindgen; -use wasm_bindgen::JsValue; -use zklink_sdk_provider::error::RpcError; -use zklink_sdk_provider::network::Network; -use zklink_sdk_provider::response::{ - AccountInfoResp, AccountSnapshotResp, BlockNumberResp, BlockOnChainResp, BlockResp, ChainResp, - FastWithdrawTxResp, ForwardTxResp, Page, SubAccountBalances, SubAccountOrders, TokenResp, - TxHashOrDetailResp, TxResp, ZkLinkTxHistory, -}; -use zklink_sdk_provider::rpc_wasm::WasmRpcClient; -use zklink_sdk_signers::zklink_signer::ZkLinkSignature; -use zklink_sdk_types::basic_types::bigunit_wrapper::BigUintSerdeWrapper; -use zklink_sdk_types::basic_types::tx_hash::TxHash; -use zklink_sdk_types::basic_types::{AccountId, BlockNumber, ChainId, SubAccountId, TokenId}; -use zklink_sdk_types::prelude::ZkLinkAddress; - -#[wasm_bindgen] -pub struct Provider { - client: WasmRpcClient, -} - -#[wasm_bindgen] -impl Provider { - #[wasm_bindgen(constructor)] - pub fn new(network: &str) -> Provider { - Provider { - client: WasmRpcClient { - server_url: Network::from_str(network).unwrap().url().to_owned(), - }, - } - } - - #[wasm_bindgen(js_name=getSupportTokens)] - pub async fn tokens(&self) -> Result { - let result: HashMap = self.client.tokens().await?; - Ok(serde_wasm_bindgen::to_value(&result)?) - } - - #[wasm_bindgen(js_name=getAccountSnapshot)] - pub async fn account_query( - &self, - account_query: AccountQueryParam, - sub_account_id: Option, - block_number: Option, - ) -> Result { - let result: AccountSnapshotResp = self - .client - .account_query( - account_query.into(), - sub_account_id.map(|id| SubAccountId(id)), - block_number.map(|number| BlockNumber(number)), - ) - .await?; - Ok(serde_wasm_bindgen::to_value(&result)?) - } - - #[wasm_bindgen(js_name=sendTransaction)] - pub async fn send_transaction( - &self, - tx: SignedTransaction, - l1_signature: Option, - l2_signature: Option, - ) -> Result { - let result: TxHash = self - .client - .send_transaction( - tx.into(), - l1_signature.map(|t| t.into()), - l2_signature.map(|s| ZkLinkSignature::from_hex(&s).unwrap()), - ) - .await?; - Ok(result.as_hex()) - } - - #[wasm_bindgen(js_name=getSupportChains)] - pub async fn get_support_chains(&self) -> Result { - let result: Vec = self.client.get_support_chains().await?; - Ok(serde_wasm_bindgen::to_value(&result)?) - } - - #[wasm_bindgen(js_name=getLatestBlockNumber)] - pub async fn block_info(&self) -> Result { - let result: BlockNumberResp = self.client.block_info().await?; - Ok(serde_wasm_bindgen::to_value(&result)?) - } - - #[wasm_bindgen(js_name=getBlockByNumber)] - pub async fn block_detail( - &self, - block_number: Option, - include_tx: bool, - include_update: bool, - ) -> Result { - let result: BlockResp = self - .client - .block_detail( - block_number.map(|b| BlockNumber(b)), - include_tx, - include_update, - ) - .await?; - Ok(serde_wasm_bindgen::to_value(&result)?) - } - - #[wasm_bindgen(js_name=getPendingBlock)] - pub async fn pending_block_detail( - &self, - last_tx_timestamp_micro: u64, - include_tx: bool, - include_update: bool, - limit: Option, - ) -> Result { - let result: Vec = self - .client - .pending_block_detail(last_tx_timestamp_micro, include_tx, include_update, limit) - .await?; - Ok(serde_wasm_bindgen::to_value(&result)?) - } - - #[wasm_bindgen(js_name=getBlockOnChainByNumber)] - pub async fn block_onchain_detail(&self, block_number: u32) -> Result { - let result: BlockOnChainResp = self - .client - .block_onchain_detail(BlockNumber(block_number)) - .await?; - Ok(serde_wasm_bindgen::to_value(&result)?) - } - - #[wasm_bindgen(js_name=getAccount)] - pub async fn account_info(&self, account_query: AccountQueryParam) -> Result { - let result: AccountInfoResp = self.client.account_info(account_query.into()).await?; - Ok(serde_wasm_bindgen::to_value(&result)?) - } - - #[wasm_bindgen(js_name=getAccountBalances)] - pub async fn account_balances( - &self, - account_id: u32, - sub_account_id: Option, - ) -> Result { - let result: SubAccountBalances = self - .client - .account_balances( - AccountId(account_id), - sub_account_id.map(|id| SubAccountId(id)), - ) - .await?; - Ok(serde_wasm_bindgen::to_value(&result)?) - } - - #[wasm_bindgen(js_name=getAccountOrderSlots)] - pub async fn account_order_slots( - &self, - account_id: u32, - sub_account_id: Option, - ) -> Result { - let result: SubAccountOrders = self - .client - .account_order_slots( - AccountId(account_id), - sub_account_id.map(|id| SubAccountId(id)), - ) - .await?; - Ok(serde_wasm_bindgen::to_value(&result)?) - } - - #[wasm_bindgen(js_name=getTokenReserve)] - pub async fn token_remain(&self, token_id: u32, mapping: bool) -> Result { - let result: HashMap = - self.client.token_remain(TokenId(token_id), mapping).await?; - Ok(serde_wasm_bindgen::to_value(&result)?) - } - - #[wasm_bindgen(js_name=getTransactionByHash)] - pub async fn tx_info(&self, hash: String, include_update: bool) -> Result { - let hash = TxHash::from_hex(&hash).map_err(|_e| RpcError::InvalidInputParameter)?; - let result: TxResp = self.client.tx_info(hash, include_update).await?; - Ok(serde_wasm_bindgen::to_value(&result)?) - } - - #[wasm_bindgen(js_name=getAccountTransactionHistory)] - pub async fn tx_history( - &self, - tx_type: L2TxType, - address: String, - page_index: u64, - page_size: u32, - ) -> Result { - let address = - ZkLinkAddress::from_hex(&address).map_err(|_e| RpcError::InvalidInputParameter)?; - let result: Page = self - .client - .tx_history(tx_type.into(), address, page_index, page_size) - .await?; - Ok(serde_wasm_bindgen::to_value(&result)?) - } - - #[wasm_bindgen(js_name=getFastWithdrawTxs)] - pub async fn tx_fast_withdraw( - &self, - last_tx_timestamp: u64, - max_txs: u32, - ) -> Result { - let result: Vec = self - .client - .tx_fast_withdraw(last_tx_timestamp, max_txs) - .await?; - Ok(serde_wasm_bindgen::to_value(&result)?) - } - - #[wasm_bindgen(js_name=pullForwardTxs)] - pub async fn pull_forward_txs( - &self, - sub_account_id: u8, - offset_id: i64, - limit: i64, - ) -> Result { - let result: Vec = self - .client - .pull_forward_txs(SubAccountId(sub_account_id), offset_id, limit) - .await?; - Ok(serde_wasm_bindgen::to_value(&result)?) - } - - #[wasm_bindgen(js_name=confirmFullExit)] - pub async fn confirm_full_exit( - &self, - tx_hash: String, - submitter_signature: String, - ) -> Result { - let hash = TxHash::from_hex(&tx_hash).map_err(|_e| RpcError::InvalidInputParameter)?; - let signature = ZkLinkSignature::from_hex(&submitter_signature) - .map_err(|_e| RpcError::InvalidInputParameter)?; - let result: bool = self.client.confirm_full_exit(hash, signature).await?; - Ok(result) - } -} diff --git a/bindings/wasm/src/rpc_client.rs b/bindings/wasm/src/rpc_client.rs new file mode 100644 index 00000000..0b99e3f7 --- /dev/null +++ b/bindings/wasm/src/rpc_client.rs @@ -0,0 +1,321 @@ +use crate::rpc_type_converter::{AccountQueryParam, SignedTransaction, TxL1Signature}; +use std::collections::HashMap; +use std::str::FromStr; +use wasm_bindgen::prelude::wasm_bindgen; +use wasm_bindgen::JsValue; +use zklink_sdk_provider::error::RpcError; +use zklink_sdk_provider::network::Network; +use zklink_sdk_provider::response::{AccountInfoResp, AccountSnapshotResp, BlockNumberResp, BlockOnChainResp, BlockResp, ChainResp, FastWithdrawTxResp, ForwardTxResp, Page, SubAccountBalances, SubAccountOrders, TokenResp, TxHashOrDetailResp, TxResp, ZkLinkTxHistory, AccountQuery}; +use zklink_sdk_signers::zklink_signer::ZkLinkSignature; +use zklink_sdk_types::basic_types::bigunit_wrapper::BigUintSerdeWrapper; +use zklink_sdk_types::basic_types::tx_hash::TxHash; +use zklink_sdk_types::basic_types::{AccountId, BlockNumber, ChainId, SubAccountId, TokenId}; +use zklink_sdk_types::prelude::ZkLinkAddress; +use zklink_sdk_types::tx_type::zklink_tx::ZkLinkTxType; +use getrandom::getrandom; +use jsonrpsee::core::params::ArrayParams; +use jsonrpsee::core::traits::ToRpcParams; +use jsonrpsee::types::request::Request; +use jsonrpsee::types::Id; +use zklink_sdk_types::signatures::TxLayer1Signature; +use zklink_sdk_types::tx_type::zklink_tx::ZkLinkTx; + +macro_rules! rpc_request { + ($method:expr,$builder:expr, $server_url:expr, $resp_type: ty) => {{ + let params = $builder + .to_rpc_params() + .map_err(RpcError::ParseParamsError)?; + let request = Request::new( + $method.into(), + params.as_ref().map(|p| p.as_ref()), + Id::Str(uuid_str().into()), + ); + let res = reqwest::Client::new() + .post($server_url) + .json(&request) + .send() + .await + .map_err(RpcError::RequestError)? + .json::>() + .await + .map_err(RpcError::ResponseError)?; + if let Some(&ref result) = res.get("result") { + let resp = serde_json::from_value::<$resp_type>(result.clone()); + match resp { + Ok(resp) => { + Ok(serde_wasm_bindgen::to_value(&resp)?) + }, + Err(_e) => { + Err(RpcError::ParseJsonError.into()) + } + } + } else { + Err(RpcError::ParseJsonError.into()) + } + }}; +} + +pub fn uuid_str() -> String { + let mut bytes = [0; 16]; + getrandom(&mut bytes).expect("RNG failure!"); + + let uuid = uuid::Builder::from_bytes(bytes) + .set_variant(uuid::Variant::RFC4122) + .set_version(uuid::Version::Random) + .build(); + + uuid.to_string() +} + +#[wasm_bindgen] +pub struct RpcClient { + server_url: String, +} + +#[wasm_bindgen] +impl RpcClient { + #[wasm_bindgen(constructor)] + pub fn new(network: &str) -> RpcClient { + RpcClient { + server_url: Network::from_str(network).unwrap().url().to_owned(), + } + } + + #[wasm_bindgen(js_name=getSupportTokens)] + pub async fn tokens(&self) -> Result { + let builder = ArrayParams::new(); + rpc_request!("getSupportTokens",builder,&self.server_url,HashMap) + } + + #[wasm_bindgen(js_name=getAccountSnapshot)] + pub async fn account_query( + &self, + account_query: AccountQueryParam, + sub_account_id: Option, + block_number: Option, + ) -> Result { + let mut builder = ArrayParams::new(); + let _ = builder.insert(AccountQuery::from(account_query)); + let _ = builder.insert(sub_account_id.map(|id| SubAccountId(id)),); + let _ = builder.insert(block_number.map(|number| BlockNumber(number))); + rpc_request!( + "getAccountSnapshot", + builder, + &self.server_url, + AccountSnapshotResp + ) + } + + #[wasm_bindgen(js_name=sendTransaction)] + pub async fn send_transaction( + &self, + tx: SignedTransaction, + l1_signature: Option, + l2_signature: Option, + ) -> Result { + let mut builder = ArrayParams::new(); + let _ = builder.insert(ZkLinkTx::from(tx)); + let _ = builder.insert(l1_signature.map(|t| TxLayer1Signature::from(t))); + let _ = builder.insert(l2_signature.map(|s| ZkLinkSignature::from_hex(&s).unwrap())); + rpc_request!("sendTransaction", builder, &self.server_url, TxHash) + } + + #[wasm_bindgen(js_name=getSupportChains)] + pub async fn get_support_chains(&self) -> Result { + let builder = ArrayParams::new(); + rpc_request!( + "getSupportChains", + builder, + &self.server_url, + Vec + ) + } + + #[wasm_bindgen(js_name=getLatestBlockNumber)] + pub async fn block_info(&self) -> Result { + let builder = ArrayParams::new(); + rpc_request!( + "getLatestBlockNumber", + builder, + &self.server_url, + BlockNumberResp + ) + } + + #[wasm_bindgen(js_name=getBlockByNumber)] + pub async fn block_detail( + &self, + block_number: Option, + include_tx: bool, + include_update: bool, + ) -> Result { + let mut builder = ArrayParams::new(); + let _ = builder.insert(block_number.map(|b| BlockNumber(b))); + let _ = builder.insert(include_tx); + let _ = builder.insert(include_update); + rpc_request!("getBlockByNumber", builder, &self.server_url, BlockResp) + } + + #[wasm_bindgen(js_name=getPendingBlock)] + pub async fn pending_block_detail( + &self, + last_tx_timestamp_micro: u64, + include_tx: bool, + include_update: bool, + limit: Option, + ) -> Result { + let mut builder = ArrayParams::new(); + let _ = builder.insert(last_tx_timestamp_micro); + let _ = builder.insert(include_tx); + let _ = builder.insert(include_update); + let _ = builder.insert(limit); + rpc_request!( + "getPendingBlock", + builder, + &self.server_url, + Vec + ) + } + + #[wasm_bindgen(js_name=getBlockOnChainByNumber)] + pub async fn block_onchain_detail(&self, block_number: u32) -> Result { + let mut builder = ArrayParams::new(); + let _ = builder.insert(BlockNumber(block_number)); + rpc_request!( + "getBlockOnChainByNumber", + builder, + &self.server_url, + BlockOnChainResp + ) + } + + #[wasm_bindgen(js_name=getAccount)] + pub async fn account_info(&self, account_query: AccountQueryParam) -> Result { + let mut builder = ArrayParams::new(); + let _ = builder.insert(AccountQuery::from(account_query)); + rpc_request!("getAccount", builder, &self.server_url, AccountInfoResp) + } + + #[wasm_bindgen(js_name=getAccountBalances)] + pub async fn account_balances( + &self, + account_id: u32, + sub_account_id: Option, + ) -> Result { + let mut builder = ArrayParams::new(); + let _ = builder.insert(AccountId(account_id)); + let _ = builder.insert(sub_account_id.map(|id| SubAccountId(id))); + rpc_request!( + "getAccountBalances", + builder, + &self.server_url, + SubAccountBalances + ) + } + + #[wasm_bindgen(js_name=getAccountOrderSlots)] + pub async fn account_order_slots( + &self, + account_id: u32, + sub_account_id: Option, + ) -> Result { + let mut builder = ArrayParams::new(); + let _ = builder.insert(AccountId(account_id)); + let _ = builder.insert(sub_account_id.map(|id| SubAccountId(id))); + rpc_request!( + "getAccountOrderSlots", + builder, + &self.server_url, + SubAccountOrders + ) + } + + #[wasm_bindgen(js_name=getTokenReserve)] + pub async fn token_remain(&self, token_id: u32, mapping: bool) -> Result { + let mut builder = ArrayParams::new(); + let _ = builder.insert(TokenId(token_id)); + let _ = builder.insert(mapping); + rpc_request!("getTokenReserve",builder,&self.server_url,HashMap) + } + + #[wasm_bindgen(js_name=getTransactionByHash)] + pub async fn tx_info(&self, hash: String, include_update: bool) -> Result { + let hash = TxHash::from_hex(&hash).map_err(|_e| RpcError::InvalidInputParameter)?; + let mut builder = ArrayParams::new(); + let _ = builder.insert(hash); + let _ = builder.insert(include_update); + rpc_request!("getTransactionByHash", builder, &self.server_url, TxResp) + } + + #[wasm_bindgen(js_name=getAccountTransactionHistory)] + pub async fn tx_history( + &self, + tx_type: ZkLinkTxType, + address: String, + page_index: u64, + page_size: u32, + ) -> Result { + let address = + ZkLinkAddress::from_hex(&address).map_err(|_e| RpcError::InvalidInputParameter)?; + let mut builder = ArrayParams::new(); + let _ = builder.insert(tx_type); + let _ = builder.insert(address); + let _ = builder.insert(page_index); + let _ = builder.insert(page_size); + rpc_request!( + "getAccountTransactionHistory", + builder, + &self.server_url, + Page + ) + } + + #[wasm_bindgen(js_name=getFastWithdrawTxs)] + pub async fn tx_fast_withdraw( + &self, + last_tx_timestamp: u64, + max_txs: u32, + ) -> Result { + let mut builder = ArrayParams::new(); + let _ = builder.insert(last_tx_timestamp); + let _ = builder.insert(max_txs); + rpc_request!( + "getFastWithdrawTxs", + builder, + &self.server_url, + Vec + ) + } + + #[wasm_bindgen(js_name=pullForwardTxs)] + pub async fn pull_forward_txs( + &self, + sub_account_id: u8, + offset_id: i64, + limit: i64, + ) -> Result { + let mut builder = ArrayParams::new(); + let _ = builder.insert(SubAccountId(sub_account_id)); + let _ = builder.insert(offset_id); + let _ = builder.insert(limit); + rpc_request!( + "pullForwardTxs", + builder, + &self.server_url, + Vec + ) + } + + #[wasm_bindgen(js_name=confirmFullExit)] + pub async fn confirm_full_exit( + &self, + tx_hash: String, + submitter_signature: String, + ) -> Result { + let hash = TxHash::from_hex(&tx_hash).map_err(|_e| RpcError::InvalidInputParameter)?; + let mut builder = ArrayParams::new(); + let _ = builder.insert(hash); + let _ = builder.insert(submitter_signature); + rpc_request!("confirmFullExit", builder, &self.server_url, bool) + } +} diff --git a/bindings/wasm/src/rpc.rs b/bindings/wasm/src/rpc_type_converter.rs similarity index 81% rename from bindings/wasm/src/rpc.rs rename to bindings/wasm/src/rpc_type_converter.rs index a170eb4f..cf9a2ef6 100644 --- a/bindings/wasm/src/rpc.rs +++ b/bindings/wasm/src/rpc_type_converter.rs @@ -9,7 +9,7 @@ use zklink_sdk_types::prelude::ZkLinkAddress; use zklink_sdk_types::signatures::TxLayer1Signature; use zklink_sdk_types::tx_type::change_pubkey::ChangePubKey; use zklink_sdk_types::tx_type::transfer::Transfer; -use zklink_sdk_types::tx_type::zklink_tx::{ZkLinkTx, ZkLinkTxType}; +use zklink_sdk_types::tx_type::zklink_tx::ZkLinkTx; #[wasm_bindgen] #[derive(Copy, Clone)] @@ -44,17 +44,6 @@ pub struct SignedTransaction { tx: JsValue, } -#[wasm_bindgen] -pub enum L2TxType { - Deposit, - FullExit, - ChangePubKey, - Transfer, - Withdraw, - ForcedExit, - OrderMatching, -} - #[wasm_bindgen] impl AccountQueryParam { #[wasm_bindgen(constructor)] @@ -130,18 +119,4 @@ impl From for ZkLinkTx { } } } -} - -impl From for ZkLinkTxType { - fn from(tx_type: L2TxType) -> ZkLinkTxType { - match tx_type { - L2TxType::Deposit => ZkLinkTxType::Deposit, - L2TxType::Transfer => ZkLinkTxType::Transfer, - L2TxType::ChangePubKey => ZkLinkTxType::ChangePubKey, - L2TxType::OrderMatching => ZkLinkTxType::OrderMatching, - L2TxType::FullExit => ZkLinkTxType::FullExit, - L2TxType::ForcedExit => ZkLinkTxType::ForcedExit, - L2TxType::Withdraw => ZkLinkTxType::Withdraw, - } - } -} +} \ No newline at end of file diff --git a/bindings/wasm/tests/test_provider.rs b/bindings/wasm/tests/test_provider.rs index aa70ad1b..d3966fcd 100644 --- a/bindings/wasm/tests/test_provider.rs +++ b/bindings/wasm/tests/test_provider.rs @@ -12,8 +12,8 @@ use zklink_sdk_types::tx_builder::ChangePubKeyBuilder; use zklink_sdk_types::tx_type::change_pubkey::ChangePubKey; use zklink_sdk_types::tx_type::zklink_tx::ZkLinkTx; use zklink_sdk_types::tx_type::ZkSignatureTrait; -use zklink_sdk_wasm::provider::Provider; -use zklink_sdk_wasm::rpc::{AccountQueryParam, AccountQueryType, SignedTransaction}; +use zklink_sdk_wasm::rpc_client::{Provider, RpcClient}; +use zklink_sdk_wasm::rpc_type_converter::{AccountQueryParam, AccountQueryType, SignedTransaction}; wasm_bindgen_test_configure!(run_in_worker); #[wasm_bindgen_test] @@ -44,7 +44,7 @@ async fn test_account_query() { #[wasm_bindgen_test] async fn test_send_change_pubkey() { web_sys::console::log_1(&JsValue::from_str("123")); - let provider = Provider::new("testnet"); + let client = RpcClient::new("testnet"); let private_key = "be725250b123a39dab5b7579334d5888987c72a58f4508062545fe6e08ca94f4"; let eth_signer = EthSigner::try_from(private_key).unwrap(); let zklink_signer = ZkLinkSigner::new_from_hex_eth_signer(private_key).unwrap(); @@ -89,7 +89,7 @@ async fn test_send_change_pubkey() { tx.sign(&zklink_signer).unwrap(); let submitter_signature = tx.signature.as_hex(); //send to zklink - let ret = provider + let ret = client .send_transaction( SignedTransaction::new( ChangePubKey::TX_TYPE, diff --git a/examples/Javascript/js-example/rpc.js b/examples/Javascript/js-example/rpc.js index f435d8e3..19702a3c 100644 --- a/examples/Javascript/js-example/rpc.js +++ b/examples/Javascript/js-example/rpc.js @@ -2,57 +2,58 @@ import init, * as wasm from "./web-dist/zklink-sdk-web.js"; async function main() { await init(); try { - let provider = new wasm.Provider("mainet"); - // 1.getSupportTokens - let tokens = await provider.getSupportTokens(); - console.log(tokens); - // 2.getAccountSnapshot - let account_id = new wasm.AccountQueryParam(wasm.AccountQueryType.AccountId, "5"); - let sub_account_id = 1; - // let block_number = 100; - let account_resp = await provider.getAccountSnapshot(account_id,sub_account_id,null); - console.log(account_resp); - // 3.sendTransaction(test on the tx example) + let client = new wasm.RpcClient("mainet"); + // // 1.getSupportTokens + // let tokens = await provider.getSupportTokens(); + // console.log(tokens); + // // 2.getAccountSnapshot + // let account_id = new wasm.AccountQueryParam(wasm.AccountQueryType.AccountId, "5"); + // let sub_account_id = 1; + // // let block_number = 100; + // let account_resp = await provider.getAccountSnapshot(account_id,sub_account_id,null); + // console.log(account_resp); + // // 3.sendTransaction(test on the tx example) // 4.getSupportChains - let chains = await provider.getSupportChains(); + let chains = await client.getSupportChains(); console.log(chains); - // 5.getLatestBlockNumber - let block_info = await provider.getLatestBlockNumber(); - console.log(block_info); - // 6.getBlockByNumber - let block_detail = await provider.getBlockByNumber(100,true,true); - console.log(block_detail); - // 7.getPendingBlock - let pending_block_info = await provider.getPendingBlock(1696743981000n,true,true,null); - console.log(pending_block_info); - // 8.getBlockOnChainByNumber - let on_chain_block_info = await provider.getBlockOnChainByNumber(100); - console.log(on_chain_block_info); - // 9.getAccount - let get_account_id = new wasm.AccountQueryParam(wasm.AccountQueryType.AccountId, "10"); - let account = await provider.getAccount(get_account_id); - console.log(account); - // 10.getAccountBalances - let balances = await provider.getAccountBalances(20,1); - console.log(balances); - // 11.getAccountOrderSlots - let slots = await provider.getAccountOrderSlots(20,1); - console.log(slots); - // 12.getTokenReserve - let reserve = await provider.getTokenReserve(18,false); - console.log(reserve); - // 13.getTransactionByHash - let tx_hash = "0x0cbeabac1a2257fb095c2465e148570e32793345442b39bf64cad4ed87475f9b"; - let tx_info = await provider.getTransactionByHash(tx_hash,false); - console.log(tx_info); - // 14.getAccountTransactionHistory - let history = await provider.getAccountTransactionHistory(wasm.L2TxType.Deposit,"0x12aFF993702B5d623977A9044686Fa1A2B0c2147",0n,5); - console.log(history); - // 15.getFastWithdrawTxs - let fast_withdraw_txs = await provider.getFastWithdrawTxs(1696743981000n,10); - console.log(fast_withdraw_txs); - // 16.pullForwardTxs - // 17.confirmFullExit + console.log(chains.length) + // // 5.getLatestBlockNumber + // let block_info = await provider.getLatestBlockNumber(); + // console.log(block_info); + // // 6.getBlockByNumber + // let block_detail = await provider.getBlockByNumber(100,true,true); + // console.log(block_detail); + // // 7.getPendingBlock + // let pending_block_info = await provider.getPendingBlock(1696743981000n,true,true,null); + // console.log(pending_block_info); + // // 8.getBlockOnChainByNumber + // let on_chain_block_info = await provider.getBlockOnChainByNumber(100); + // console.log(on_chain_block_info); + // // 9.getAccount + // let get_account_id = new wasm.AccountQueryParam(wasm.AccountQueryType.AccountId, "10"); + // let account = await provider.getAccount(get_account_id); + // console.log(account); + // // 10.getAccountBalances + // let balances = await provider.getAccountBalances(20,1); + // console.log(balances); + // // 11.getAccountOrderSlots + // let slots = await provider.getAccountOrderSlots(20,1); + // console.log(slots); + // // 12.getTokenReserve + // let reserve = await provider.getTokenReserve(18,false); + // console.log(reserve); + // // 13.getTransactionByHash + // let tx_hash = "0x0cbeabac1a2257fb095c2465e148570e32793345442b39bf64cad4ed87475f9b"; + // let tx_info = await provider.getTransactionByHash(tx_hash,false); + // console.log(tx_info); + // // 14.getAccountTransactionHistory + // let history = await provider.getAccountTransactionHistory(wasm.L2TxType.Deposit,"0x12aFF993702B5d623977A9044686Fa1A2B0c2147",0n,5); + // console.log(history); + // // 15.getFastWithdrawTxs + // let fast_withdraw_txs = await provider.getFastWithdrawTxs(1696743981000n,10); + // console.log(fast_withdraw_txs); + // // 16.pullForwardTxs + // // 17.confirmFullExit } catch (error) { console.error(error); } diff --git a/provider/src/error.rs b/provider/src/error.rs index 80f33d75..ea73a27d 100644 --- a/provider/src/error.rs +++ b/provider/src/error.rs @@ -1,5 +1,6 @@ use jsonrpsee::core::error::Error as jsonrpseeError; use thiserror::Error; +use wasm_bindgen::JsValue; #[derive(Debug, Error)] pub enum RpcError { @@ -16,3 +17,10 @@ pub enum RpcError { #[error("Parse json value error")] ParseJsonError, } + + +impl From for JsValue { + fn from(error: RpcError) -> Self { + JsValue::from_str(&format!("error: {error}")) + } +} \ No newline at end of file diff --git a/provider/src/lib.rs b/provider/src/lib.rs index ef57f599..56f1f87a 100644 --- a/provider/src/lib.rs +++ b/provider/src/lib.rs @@ -4,8 +4,6 @@ pub mod response; #[cfg(not(feature = "ffi"))] #[cfg(not(target_arch = "wasm32"))] pub mod rpc; -#[cfg(target_arch = "wasm32")] -pub mod rpc_wasm; #[cfg(not(feature = "ffi"))] #[cfg(not(target_arch = "wasm32"))] diff --git a/provider/src/rpc_wasm.rs b/provider/src/rpc_wasm.rs deleted file mode 100644 index 9a3de3e8..00000000 --- a/provider/src/rpc_wasm.rs +++ /dev/null @@ -1,463 +0,0 @@ -use crate::error::RpcError; -use crate::response::{ - AccountInfoResp, AccountQuery, AccountSnapshotResp, BlockNumberResp, BlockOnChainResp, - BlockResp, ChainResp, FastWithdrawTxResp, ForwardTxResp, Page, SubAccountBalances, - SubAccountOrders, TokenResp, TxHashOrDetailResp, TxResp, ZkLinkTxHistory, -}; -use getrandom::getrandom; -use jsonrpsee::core::params::ArrayParams; -use jsonrpsee::core::traits::ToRpcParams; -use jsonrpsee::types::request::Request; -use jsonrpsee::types::Id; -use std::collections::HashMap; -use wasm_bindgen::JsValue; -use zklink_sdk_signers::zklink_signer::ZkLinkSignature; -use zklink_sdk_types::basic_types::bigunit_wrapper::BigUintSerdeWrapper; -use zklink_sdk_types::basic_types::{ - AccountId, BlockNumber, ChainId, SubAccountId, TokenId, ZkLinkAddress, -}; -use zklink_sdk_types::prelude::TxHash; -use zklink_sdk_types::signatures::TxLayer1Signature; -use zklink_sdk_types::tx_type::zklink_tx::{ZkLinkTx, ZkLinkTxType}; - -impl From for JsValue { - fn from(error: RpcError) -> Self { - JsValue::from_str(&format!("error: {error}")) - } -} - -macro_rules! make_rpc_request { - ($method:expr,$builder:expr, $server_url:expr, $resp_type: ty) => {{ - let params = $builder - .to_rpc_params() - .map_err(RpcError::ParseParamsError)?; - let request = Request::new( - $method.into(), - params.as_ref().map(|p| p.as_ref()), - Id::Str(uuid_str().into()), - ); - let res = reqwest::Client::new() - .post($server_url) - .json(&request) - .send() - .await - .map_err(RpcError::RequestError)? - .json::>() - .await - .map_err(RpcError::ResponseError)?; - if let Some(&ref result) = res.get("result") { - let resp: HashMap = - serde_json::from_value(result.clone()).map_err(|_e| RpcError::ParseJsonError)?; - Ok(resp) - } else { - Err(RpcError::ParseJsonError) - } - }}; -} - -pub fn uuid_str() -> String { - let mut bytes = [0; 16]; - getrandom(&mut bytes).expect("RNG failure!"); - - let uuid = uuid::Builder::from_bytes(bytes) - .set_variant(uuid::Variant::RFC4122) - .set_version(uuid::Version::Random) - .build(); - - uuid.to_string() -} - -pub struct WasmRpcClient { - pub server_url: String, -} - -impl WasmRpcClient { - pub fn new(server_url: String) -> Self { - Self { server_url } - } - - pub async fn tokens(&self) -> Result, RpcError> { - let builder = ArrayParams::new(); - make_rpc_request!("getSupportTokens",builder,&self.server_url,HashMap) - } - - pub async fn account_query( - &self, - account_query: AccountQuery, - sub_account_id: Option, - block_number: Option, - ) -> Result { - let mut builder = ArrayParams::new(); - let _ = builder.insert(account_query); - let _ = builder.insert(sub_account_id); - let _ = builder.insert(block_number); - make_rpc_request!( - "getAccountSnapshot", - builder, - &self.server_url, - AccountSnapshotResp - ) - } - - pub async fn send_transaction( - &self, - tx: ZkLinkTx, - eth_signature: Option, - submitter_signature: Option, - ) -> Result { - let mut builder = ArrayParams::new(); - let _ = builder.insert(tx); - let _ = builder.insert(eth_signature); - let _ = builder.insert(submitter_signature); - make_rpc_request!("sendTransaction", builder, &self.server_url, TxHash) - } - - pub async fn get_support_chains(&self) -> Result, RpcError> { - let builder = ArrayParams::new(); - make_rpc_request!( - "getSupportChains", - builder, - &self.server_url, - Vec - ) - } - - pub async fn block_info(&self) -> Result { - let builder = ArrayParams::new(); - make_rpc_request!( - "getLatestBlockNumber", - builder, - &self.server_url, - BlockNumberResp - ) - } - - pub async fn block_detail( - &self, - block_number: Option, - include_tx: bool, - include_update: bool, - ) -> Result { - let mut builder = ArrayParams::new(); - let _ = builder.insert(block_number); - let _ = builder.insert(include_tx); - let _ = builder.insert(include_update); - make_rpc_request!("getBlockByNumber", builder, &self.server_url, BlockResp) - } - - pub async fn pending_block_detail( - &self, - last_tx_timestamp_micro: u64, - include_tx: bool, - include_update: bool, - limit: Option, - ) -> Result, RpcError> { - let mut builder = ArrayParams::new(); - let _ = builder.insert(last_tx_timestamp_micro); - let _ = builder.insert(include_tx); - let _ = builder.insert(include_update); - let _ = builder.insert(limit); - make_rpc_request!( - "getPendingBlock", - builder, - &self.server_url, - Vec - ) - } - - pub async fn block_onchain_detail( - &self, - block_number: BlockNumber, - ) -> Result { - let mut builder = ArrayParams::new(); - let _ = builder.insert(block_number); - make_rpc_request!( - "getBlockOnChainByNumber", - builder, - &self.server_url, - BlockOnChainResp - ) - } - - pub async fn account_info( - &self, - account_query: AccountQuery, - ) -> Result { - let mut builder = ArrayParams::new(); - let _ = builder.insert(account_query); - make_rpc_request!("getAccount", builder, &self.server_url, AccountInfoResp) - } - - pub async fn account_balances( - &self, - account_id: AccountId, - sub_account_id: Option, - ) -> Result { - let mut builder = ArrayParams::new(); - let _ = builder.insert(account_id); - let _ = builder.insert(sub_account_id); - make_rpc_request!( - "getAccountBalances", - builder, - &self.server_url, - SubAccountBalances - ) - } - - pub async fn account_order_slots( - &self, - account_id: AccountId, - sub_account_id: Option, - ) -> Result { - let mut builder = ArrayParams::new(); - let _ = builder.insert(account_id); - let _ = builder.insert(sub_account_id); - make_rpc_request!( - "getAccountOrderSlots", - builder, - &self.server_url, - SubAccountOrders - ) - } - - pub async fn token_remain( - &self, - token_id: TokenId, - mapping: bool, - ) -> Result, RpcError> { - let mut builder = ArrayParams::new(); - let _ = builder.insert(token_id); - let _ = builder.insert(mapping); - make_rpc_request!("getTokenReserve",builder,&self.server_url,HashMap) - } - - pub async fn tx_info(&self, hash: TxHash, include_update: bool) -> Result { - let mut builder = ArrayParams::new(); - let _ = builder.insert(hash); - let _ = builder.insert(include_update); - make_rpc_request!("getTransactionByHash", builder, &self.server_url, TxResp) - } - - pub async fn tx_history( - &self, - tx_type: ZkLinkTxType, - address: ZkLinkAddress, - page_index: u64, - page_size: u32, - ) -> Result, RpcError> { - let mut builder = ArrayParams::new(); - let _ = builder.insert(tx_type); - let _ = builder.insert(address); - let _ = builder.insert(page_index); - let _ = builder.insert(page_size); - make_rpc_request!( - "getAccountTransactionHistory", - builder, - &self.server_url, - Page - ) - } - - pub async fn tx_fast_withdraw( - &self, - last_tx_timestamp: u64, - max_txs: u32, - ) -> Result, RpcError> { - let mut builder = ArrayParams::new(); - let _ = builder.insert(last_tx_timestamp); - let _ = builder.insert(max_txs); - make_rpc_request!( - "getFastWithdrawTxs", - builder, - &self.server_url, - Vec - ) - } - - pub async fn pull_forward_txs( - &self, - sub_account_id: SubAccountId, - offset_id: i64, - limit: i64, - ) -> Result, RpcError> { - let mut builder = ArrayParams::new(); - let _ = builder.insert(sub_account_id); - let _ = builder.insert(offset_id); - let _ = builder.insert(limit); - make_rpc_request!( - "pullForwardTxs", - builder, - &self.server_url, - Vec - ) - } - - pub async fn confirm_full_exit( - &self, - tx_hash: TxHash, - submitter_signature: ZkLinkSignature, - ) -> Result { - let mut builder = ArrayParams::new(); - let _ = builder.insert(tx_hash); - let _ = builder.insert(submitter_signature); - make_rpc_request!("confirmFullExit", builder, &self.server_url, bool) - } -} - -#[cfg(test)] -mod test { - use super::WasmRpcClient; - use crate::network::Network; - use crate::response::ZkLinkTxHistory; - use crate::rpc::ZkLinkRpcClient; - use crate::ZkLinkRpcProvider; - use std::str::FromStr; - use std::sync::Arc; - use std::time::Duration; - use zklink_sdk_signers::eth_signer::EthSigner; - use zklink_sdk_signers::zklink_signer::{PubKeyHash, ZkLinkSigner}; - use zklink_sdk_types::basic_types::BigUint; - use zklink_sdk_types::basic_types::{ - AccountId, ChainId, Nonce, SubAccountId, TimeStamp, TokenId, ZkLinkAddress, - }; - use zklink_sdk_types::prelude::ZkLinkTxType; - use zklink_sdk_types::signatures::TxLayer1Signature; - use zklink_sdk_types::tx_builder::{ChangePubKeyBuilder, TransferBuilder}; - use zklink_sdk_types::tx_type::change_pubkey::ChangePubKey; - use zklink_sdk_types::tx_type::transfer::Transfer; - use zklink_sdk_types::tx_type::zklink_tx::ZkLinkTx; - use zklink_sdk_types::tx_type::ZkSignatureTrait; - - #[tokio::test] - async fn test_tokens() { - let client = WasmRpcClient::new("https://api-v1.zk.link".to_owned()); - let ret = client.tokens().await.unwrap(); - println!("{:?}", ret); - } - - #[tokio::test] - async fn test_tx_history() { - let client = WasmRpcClient::new("https://api-v1.zk.link".to_owned()); - let ret = client - .tx_history( - ZkLinkTxType::Deposit, - ZkLinkAddress::from_hex("0x12aFF993702B5d623977A9044686Fa1A2B0c2147").unwrap(), - 0, - 1, - ) - .await - .unwrap(); - println!("{:?}", ret); - } - - #[tokio::test] - async fn test_send_change_pubkey() { - let private_key = "be725250b123a39dab5b7579334d5888987c72a58f4508062545fe6e08ca94f4"; - let eth_signer = EthSigner::try_from(private_key).unwrap(); - let zklink_signer = ZkLinkSigner::new_from_hex_eth_signer(private_key).unwrap(); - let main_contract = "0x5505a8cD4594Dbf79d8C59C0Df1414AB871CA896"; - let l1_client_id = 80001; - let new_pubkey_hash = "0xd8d5fb6a6caef06aa3dc2abdcdc240987e5330fe"; - let ts = 1696595303; - //auth type 'ECDSA' - let builder = ChangePubKeyBuilder { - chain_id: ChainId(1), - account_id: AccountId(10), - sub_account_id: SubAccountId(1), - new_pubkey_hash: PubKeyHash::from_hex(new_pubkey_hash).unwrap(), - fee_token: TokenId(18), - fee: BigUint::from(100000000000000u64), - nonce: Nonce(1), - eth_signature: None, - timestamp: TimeStamp(ts), - }; - let mut change_pubkey = ChangePubKey::new(builder); - let message = change_pubkey - .to_eip712_request_payload( - l1_client_id, - &ZkLinkAddress::from_str(&main_contract).unwrap(), - ) - .unwrap(); - let signature = eth_signer - .sign_message(message.raw_data.as_bytes()) - .unwrap(); - let builder_with_sig = ChangePubKeyBuilder { - chain_id: ChainId(1), - account_id: AccountId(10), - sub_account_id: SubAccountId(1), - new_pubkey_hash: PubKeyHash::from_hex(new_pubkey_hash).unwrap(), - fee_token: TokenId(18), - fee: BigUint::from(100000000000000u64), - nonce: Nonce(1), - eth_signature: Some(signature), - timestamp: TimeStamp(ts), - }; - let mut tx = ChangePubKey::new(builder_with_sig); - tx.sign(&zklink_signer).unwrap(); - - // for wasm - // let client = WasmRpcClient::new("https://aws-gw-v2.zk.link".to_owned()); - // client.send_transaction(ZkLinkTx::ChangePubKey(Box::new(tx.clone())),None,Some(tx.signature.clone())).await.unwrap(); - - //use jsonrpsee - let client = ZkLinkRpcProvider::new(Network::TestNet, Duration::from_secs(3)); - let ret = client - .tx_submit( - ZkLinkTx::ChangePubKey(Box::new(tx.clone())), - None, - Some(tx.signature.clone()), - ) - .await - .unwrap(); - } - - #[tokio::test] - async fn test_send_transfer() { - let private_key = "be725250b123a39dab5b7579334d5888987c72a58f4508062545fe6e08ca94f4"; - let eth_signer = EthSigner::try_from(private_key).unwrap(); - let zklink_signer = ZkLinkSigner::new_from_hex_eth_signer(private_key).unwrap(); - let to_address = "0x5505a8cD4594Dbf79d8C59C0Df1414AB871CA896"; - let l1_client_id = 80001; - let ts = 1696595303; - //auth type 'ECDSA' - let builder = TransferBuilder { - account_id: AccountId(10), - to_address: ZkLinkAddress::from_str(to_address).unwrap(), - from_sub_account_id: SubAccountId(1), - to_sub_account_id: SubAccountId(1), - token: TokenId(18), - fee: BigUint::from(100000000000000u64), - nonce: Nonce(1), - timestamp: TimeStamp(ts), - amount: BigUint::from(1000000000000000u64), - }; - let mut transfer = Transfer::new(builder); - let eth_signature = eth_signer - .sign_message(transfer.get_eth_sign_msg("USDT").as_bytes()) - .unwrap(); - transfer.sign(&zklink_signer).unwrap(); - - let submiiter_signature = transfer - .submitter_signature(Arc::new(zklink_signer)) - .unwrap(); - // for wasm - let client = WasmRpcClient::new("https://aws-gw-v2.zk.link".to_owned()); - client - .send_transaction( - ZkLinkTx::Transfer(Box::new(transfer.clone())), - Some(TxLayer1Signature::EthereumSignature(eth_signature)), - Some(submiiter_signature), - ) - .await - .unwrap(); - - //use jsonrpsee - // let client = ZkLinkRpcProvider::new(Network::TestNet, Duration::from_secs(3)); - // let ret = client.tx_submit( - // ZkLinkTx::Transfer(Box::new(transfer.clone())), - // None, - // Some(transfer.signature.clone()) - // ) - // .await.unwrap(); - } -} diff --git a/signers/src/eth_signer/pk_signer.rs b/signers/src/eth_signer/pk_signer.rs index 807f344a..d48deaed 100644 --- a/signers/src/eth_signer/pk_signer.rs +++ b/signers/src/eth_signer/pk_signer.rs @@ -7,7 +7,9 @@ use ethers::types::transaction::eip2718::TypedTransaction; use ethers::types::TxHash; use ethers::utils::hash_message; use k256::ecdsa::SigningKey; +use wasm_bindgen::prelude::wasm_bindgen; +#[wasm_bindgen] #[derive(Clone)] pub struct EthSigner { private_key: H256, diff --git a/types/Cargo.toml b/types/Cargo.toml index 8bd93081..fe776dba 100644 --- a/types/Cargo.toml +++ b/types/Cargo.toml @@ -14,6 +14,7 @@ thiserror = "1.0" validator = { version = "0.15", features = ["derive"] } zklink_sdk_signers = { path = "../signers" } zklink_sdk_utils = { path = "../utils" } +wasm-bindgen = { version = "0.2.87", features = ["serde-serialize"] } [dev-dependencies] diff --git a/types/src/tx_type/zklink_tx.rs b/types/src/tx_type/zklink_tx.rs index efb21123..052d35b5 100644 --- a/types/src/tx_type/zklink_tx.rs +++ b/types/src/tx_type/zklink_tx.rs @@ -10,8 +10,10 @@ use crate::tx_type::order_matching::OrderMatching; use crate::tx_type::transfer::Transfer; use crate::tx_type::withdraw::Withdraw; use crate::tx_type::TxTrait; +use wasm_bindgen::prelude::wasm_bindgen; /// A set of L2 transaction type supported by the zklink network. +#[wasm_bindgen] #[derive(Debug, Clone, Serialize, Deserialize)] pub enum ZkLinkTxType { Deposit, From faffc6c7b0950112315a7f64159d72ce776a9d17 Mon Sep 17 00:00:00 2001 From: nickwest Date: Tue, 10 Oct 2023 21:54:15 -0700 Subject: [PATCH 4/6] Make the calling processes of wasm and golang consistent --- bindings/wasm/Cargo.toml | 1 + bindings/wasm/src/crypto.rs | 77 ---------- bindings/wasm/src/lib.rs | 2 +- bindings/wasm/src/rpc_client.rs | 33 ++--- bindings/wasm/src/rpc_type_converter.rs | 2 +- bindings/wasm/src/signer.rs | 76 ++++++++++ bindings/wasm/src/tx_types/change_pubkey.rs | 131 ++++++++++-------- bindings/wasm/src/tx_types/transfer.rs | 74 +++++----- .../tests/{test_provider.rs => test_rpc.rs} | 0 .../Javascript/js-example/1_change_pubkey.js | 58 ++------ examples/Javascript/js-example/2_transfer.js | 25 ++-- examples/Javascript/js-example/index.html | 5 +- interface/Cargo.toml | 1 + interface/src/error.rs | 9 ++ provider/Cargo.toml | 1 - provider/src/error.rs | 3 +- signers/src/eth_signer/pk_signer.rs | 2 - types/Cargo.toml | 2 +- types/src/error.rs | 9 ++ 19 files changed, 250 insertions(+), 261 deletions(-) delete mode 100644 bindings/wasm/src/crypto.rs create mode 100644 bindings/wasm/src/signer.rs rename bindings/wasm/tests/{test_provider.rs => test_rpc.rs} (100%) diff --git a/bindings/wasm/Cargo.toml b/bindings/wasm/Cargo.toml index 664c1351..80e781a1 100644 --- a/bindings/wasm/Cargo.toml +++ b/bindings/wasm/Cargo.toml @@ -11,6 +11,7 @@ crate-type = ["cdylib","rlib"] zklink_sdk_signers = { path = "../../signers" } zklink_sdk_types = { path = "../../types" } zklink_sdk_provider = { path = "../../provider" } +zklink_sdk_interface = { path = "../../interface" } serde_json = "1.0" wasm-bindgen = { version = "0.2.87",features = ["serde-serialize"] } serde-wasm-bindgen = "0.5" diff --git a/bindings/wasm/src/crypto.rs b/bindings/wasm/src/crypto.rs deleted file mode 100644 index 3aab81be..00000000 --- a/bindings/wasm/src/crypto.rs +++ /dev/null @@ -1,77 +0,0 @@ -use wasm_bindgen::prelude::*; -use zklink_sdk_signers::eth_signer::pk_signer::EthSigner; -use zklink_sdk_signers::zklink_signer::pk_signer::ZkLinkSigner as Signer; -use zklink_sdk_signers::zklink_signer::signature::ZkLinkSignature as Signature; - -#[wasm_bindgen] -pub struct EthPrivateKeySigner { - inner: EthSigner, -} - -#[wasm_bindgen] -impl EthPrivateKeySigner { - #[wasm_bindgen(js_name=newFromHexPrivateKey)] - pub fn new_from_hex_pk(private_key: &str) -> Result { - let signer = EthSigner::try_from(private_key)?; - Ok(Self { inner: signer }) - } - - #[wasm_bindgen(js_name=getAddress)] - pub fn get_address(&self) -> String { - let address = self.inner.get_address(); - format!("{:?}", address) - } - #[wasm_bindgen(js_name=signMessage)] - pub fn sign_message(&self, msg: &[u8]) -> Result { - let signature = self.inner.sign_message(msg)?; - Ok(signature.as_hex()) - } -} - -#[wasm_bindgen] -pub struct ZklinkSigner { - inner: Signer, -} - -#[wasm_bindgen] -impl ZklinkSigner { - #[wasm_bindgen(js_name=newRand)] - pub fn new_rand() -> Result { - let zklink_signer = Signer::new()?; - Ok(ZklinkSigner { - inner: zklink_signer, - }) - } - - #[wasm_bindgen(js_name=newFromEthSigner)] - pub fn new_from_hex_eth_signer(eth_hex_private_key: &str) -> Result { - let zklink_signer = Signer::new_from_hex_eth_signer(eth_hex_private_key)?; - Ok(ZklinkSigner { - inner: zklink_signer, - }) - } - - #[wasm_bindgen] - pub fn sign(&self, msg: &[u8]) -> Result { - let signature = self.inner.sign_musig(msg)?; - Ok(signature.as_hex()) - } -} - -#[wasm_bindgen] -pub struct ZklinkSignature { - inner: Signature, -} - -#[wasm_bindgen] -impl ZklinkSignature { - #[wasm_bindgen(js_name=newFromHexStr)] - pub fn new_from_hex_str(signature_str: &str) -> Result { - let signature = Signature::from_hex(signature_str)?; - Ok(ZklinkSignature { inner: signature }) - } - #[wasm_bindgen] - pub fn verify(&self, msg: &[u8]) -> bool { - self.inner.verify_musig(msg) - } -} diff --git a/bindings/wasm/src/lib.rs b/bindings/wasm/src/lib.rs index 047377a2..5bb43983 100644 --- a/bindings/wasm/src/lib.rs +++ b/bindings/wasm/src/lib.rs @@ -1,7 +1,7 @@ #![cfg(target_arch = "wasm32")] -pub mod crypto; pub mod rpc_client; pub mod rpc_type_converter; +pub mod signer; pub mod tx_types; // pub mod wallet; // pub mod error; diff --git a/bindings/wasm/src/rpc_client.rs b/bindings/wasm/src/rpc_client.rs index 0b99e3f7..99ad24d6 100644 --- a/bindings/wasm/src/rpc_client.rs +++ b/bindings/wasm/src/rpc_client.rs @@ -1,24 +1,28 @@ use crate::rpc_type_converter::{AccountQueryParam, SignedTransaction, TxL1Signature}; +use getrandom::getrandom; +use jsonrpsee::core::params::ArrayParams; +use jsonrpsee::core::traits::ToRpcParams; +use jsonrpsee::types::request::Request; +use jsonrpsee::types::Id; use std::collections::HashMap; use std::str::FromStr; use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::JsValue; use zklink_sdk_provider::error::RpcError; use zklink_sdk_provider::network::Network; -use zklink_sdk_provider::response::{AccountInfoResp, AccountSnapshotResp, BlockNumberResp, BlockOnChainResp, BlockResp, ChainResp, FastWithdrawTxResp, ForwardTxResp, Page, SubAccountBalances, SubAccountOrders, TokenResp, TxHashOrDetailResp, TxResp, ZkLinkTxHistory, AccountQuery}; +use zklink_sdk_provider::response::{ + AccountInfoResp, AccountQuery, AccountSnapshotResp, BlockNumberResp, BlockOnChainResp, + BlockResp, ChainResp, FastWithdrawTxResp, ForwardTxResp, Page, SubAccountBalances, + SubAccountOrders, TokenResp, TxHashOrDetailResp, TxResp, ZkLinkTxHistory, +}; use zklink_sdk_signers::zklink_signer::ZkLinkSignature; use zklink_sdk_types::basic_types::bigunit_wrapper::BigUintSerdeWrapper; use zklink_sdk_types::basic_types::tx_hash::TxHash; use zklink_sdk_types::basic_types::{AccountId, BlockNumber, ChainId, SubAccountId, TokenId}; use zklink_sdk_types::prelude::ZkLinkAddress; -use zklink_sdk_types::tx_type::zklink_tx::ZkLinkTxType; -use getrandom::getrandom; -use jsonrpsee::core::params::ArrayParams; -use jsonrpsee::core::traits::ToRpcParams; -use jsonrpsee::types::request::Request; -use jsonrpsee::types::Id; use zklink_sdk_types::signatures::TxLayer1Signature; use zklink_sdk_types::tx_type::zklink_tx::ZkLinkTx; +use zklink_sdk_types::tx_type::zklink_tx::ZkLinkTxType; macro_rules! rpc_request { ($method:expr,$builder:expr, $server_url:expr, $resp_type: ty) => {{ @@ -42,12 +46,8 @@ macro_rules! rpc_request { if let Some(&ref result) = res.get("result") { let resp = serde_json::from_value::<$resp_type>(result.clone()); match resp { - Ok(resp) => { - Ok(serde_wasm_bindgen::to_value(&resp)?) - }, - Err(_e) => { - Err(RpcError::ParseJsonError.into()) - } + Ok(resp) => Ok(serde_wasm_bindgen::to_value(&resp)?), + Err(_e) => Err(RpcError::ParseJsonError.into()), } } else { Err(RpcError::ParseJsonError.into()) @@ -96,7 +96,7 @@ impl RpcClient { ) -> Result { let mut builder = ArrayParams::new(); let _ = builder.insert(AccountQuery::from(account_query)); - let _ = builder.insert(sub_account_id.map(|id| SubAccountId(id)),); + let _ = builder.insert(sub_account_id.map(|id| SubAccountId(id))); let _ = builder.insert(block_number.map(|number| BlockNumber(number))); rpc_request!( "getAccountSnapshot", @@ -109,12 +109,13 @@ impl RpcClient { #[wasm_bindgen(js_name=sendTransaction)] pub async fn send_transaction( &self, - tx: SignedTransaction, + tx: JsValue, l1_signature: Option, l2_signature: Option, ) -> Result { let mut builder = ArrayParams::new(); - let _ = builder.insert(ZkLinkTx::from(tx)); + let zklink_tx: ZkLinkTx = serde_wasm_bindgen::from_value(tx)?; + let _ = builder.insert(zklink_tx); let _ = builder.insert(l1_signature.map(|t| TxLayer1Signature::from(t))); let _ = builder.insert(l2_signature.map(|s| ZkLinkSignature::from_hex(&s).unwrap())); rpc_request!("sendTransaction", builder, &self.server_url, TxHash) diff --git a/bindings/wasm/src/rpc_type_converter.rs b/bindings/wasm/src/rpc_type_converter.rs index cf9a2ef6..707ec35c 100644 --- a/bindings/wasm/src/rpc_type_converter.rs +++ b/bindings/wasm/src/rpc_type_converter.rs @@ -119,4 +119,4 @@ impl From for ZkLinkTx { } } } -} \ No newline at end of file +} diff --git a/bindings/wasm/src/signer.rs b/bindings/wasm/src/signer.rs new file mode 100644 index 00000000..0d02dbec --- /dev/null +++ b/bindings/wasm/src/signer.rs @@ -0,0 +1,76 @@ +use crate::tx_types::change_pubkey::{ChangePubKey, Create2Data}; +use crate::tx_types::transfer::Transfer; +use std::str::FromStr; +use wasm_bindgen::prelude::wasm_bindgen; +use wasm_bindgen::JsValue; +use zklink_sdk_interface::signer::Signer as InterfaceSigner; +use zklink_sdk_signers::eth_signer::EthSigner; +use zklink_sdk_signers::zklink_signer::ZkLinkSigner; +use zklink_sdk_types::basic_types::ZkLinkAddress; +use zklink_sdk_types::tx_type::change_pubkey::ChangePubKey as TxChangePubKey; +use zklink_sdk_types::tx_type::change_pubkey::Create2Data as ChangePubKeyCreate2Data; +use zklink_sdk_types::tx_type::transfer::Transfer as TxTransfer; +use zklink_sdk_types::tx_type::zklink_tx::ZkLinkTx; + +#[wasm_bindgen] +pub struct Signer { + inner: InterfaceSigner, +} + +#[wasm_bindgen] +impl Signer { + #[wasm_bindgen(constructor)] + pub fn new(private_key: &str) -> Result { + let inner = InterfaceSigner::new(private_key)?; + Ok(Signer { inner }) + } + + #[wasm_bindgen(js_name=signChangePubkeyWithEthEcdsaAuth)] + pub fn sign_change_pubkey_with_eth_ecdsa_auth( + &self, + tx: ChangePubKey, + l1_client_id: u32, + main_contract: &str, + ) -> Result { + let inner_tx = tx.get_inner_tx()?; + let change_pubkey: TxChangePubKey = serde_wasm_bindgen::from_value(inner_tx)?; + let contract_address = ZkLinkAddress::from_hex(main_contract)?; + let signature = self.inner.sign_change_pubkey_with_eth_ecdsa_auth( + change_pubkey, + l1_client_id, + contract_address, + )?; + Ok(serde_wasm_bindgen::to_value(&signature)?) + } + + #[wasm_bindgen(js_name=signChangePubkeyWithCreate2DataAuth)] + pub fn sign_change_pubkey_with_create2data_auth( + &self, + tx: ChangePubKey, + create2_data: Create2Data, + ) -> Result { + let inner_tx = tx.get_inner_tx()?; + let change_pubkey: TxChangePubKey = serde_wasm_bindgen::from_value(inner_tx)?; + let inner_data = create2_data.get_inner_data()?; + let create2_data: ChangePubKeyCreate2Data = serde_wasm_bindgen::from_value(inner_data)?; + let signature = self + .inner + .sign_change_pubkey_with_create2data_auth(change_pubkey, create2_data)?; + Ok(serde_wasm_bindgen::to_value(&signature)?) + } + + #[wasm_bindgen(js_name=signTransfer)] + pub fn sign_transfer(&self, tx: Transfer, token_symbol: &str) -> Result { + let inner_tx = tx.get_inner_tx()?; + let transfer: TxTransfer = serde_wasm_bindgen::from_value(inner_tx)?; + let signature = self.inner.sign_transfer(transfer, token_symbol)?; + Ok(serde_wasm_bindgen::to_value(&signature)?) + } + + #[wasm_bindgen(js_name=submitterSignature)] + pub fn submitter_signature(&self, tx: JsValue) -> Result { + let zklink_tx: ZkLinkTx = serde_wasm_bindgen::from_value(tx)?; + let zklink_signature = self.inner.submitter_signature(&zklink_tx)?; + Ok(zklink_signature.as_hex()) + } +} diff --git a/bindings/wasm/src/tx_types/change_pubkey.rs b/bindings/wasm/src/tx_types/change_pubkey.rs index 96aaec6a..d7d617f9 100644 --- a/bindings/wasm/src/tx_types/change_pubkey.rs +++ b/bindings/wasm/src/tx_types/change_pubkey.rs @@ -1,13 +1,15 @@ -use crate::crypto::ZklinkSigner; use std::str::FromStr; use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::JsValue; use zklink_sdk_signers::eth_signer::packed_eth_signature::PackedEthSignature; use zklink_sdk_signers::zklink_signer::pubkey_hash::PubKeyHash; use zklink_sdk_types::basic_types::{AccountId, BigUint, TokenId, ZkLinkAddress}; -use zklink_sdk_types::prelude::{ChainId, Nonce, SubAccountId, TimeStamp, ZkLinkSignature}; -use zklink_sdk_types::tx_builder::ChangePubKeyBuilder; -use zklink_sdk_types::tx_type::change_pubkey::ChangePubKey as ChangePubkeyTx; +use zklink_sdk_types::error::TypeError; +use zklink_sdk_types::prelude::{ChainId, Nonce, SubAccountId, TimeStamp, ZkLinkSignature, H256}; +use zklink_sdk_types::tx_builder::ChangePubKeyBuilder as TxChangePubKeyBuilder; +use zklink_sdk_types::tx_type::change_pubkey::{ + ChangePubKey as ChangePubkeyTx, Create2Data as ChangePubKeyCreate2Data, +}; use zklink_sdk_types::tx_type::TxTrait; #[wasm_bindgen] @@ -17,84 +19,103 @@ pub enum EthAuthType { EthCREATE2, } +#[wasm_bindgen] +pub struct Create2Data { + inner: ChangePubKeyCreate2Data, +} + #[wasm_bindgen] pub struct ChangePubKey { inner: ChangePubkeyTx, } #[wasm_bindgen] -impl ChangePubKey { +impl Create2Data { #[wasm_bindgen(constructor)] - pub fn new( - chain_id: u8, - account_id: u32, - sub_account_id: u8, - new_pubkey_hash: String, - fee_token: u32, - fee: String, - nonce: u32, - eth_signature: Option, - ts: u32, - ) -> Result { - let eth_signature = if let Some(signature) = eth_signature { - Some(PackedEthSignature::from_hex(&signature)?) - } else { - None - }; - let change_pubkey_builder = ChangePubKeyBuilder { - chain_id: ChainId(chain_id), - account_id: AccountId(account_id), - sub_account_id: SubAccountId(sub_account_id), - new_pubkey_hash: PubKeyHash::from_hex(&new_pubkey_hash).unwrap(), - fee_token: TokenId(fee_token), - fee: BigUint::from_str(&fee).unwrap(), - nonce: Nonce(nonce), - eth_signature, - timestamp: TimeStamp(ts), + pub fn new(creator_address: &str, salt: &str, code_hash: &str) -> Result { + let create2_data = ChangePubKeyCreate2Data { + creator_address: ZkLinkAddress::from_hex(creator_address)?, + salt_arg: H256::from_str(&salt) + .map_err(|e| TypeError::DecodeFromHexErr(e.to_string()))?, + code_hash: H256::from_str(code_hash) + .map_err(|e| TypeError::DecodeFromHexErr(e.to_string()))?, }; - Ok(ChangePubKey { - inner: ChangePubkeyTx::new(change_pubkey_builder), + Ok(Create2Data { + inner: create2_data, }) } - #[wasm_bindgen(js_name=getTxType)] - pub fn get_tx_type(&self) -> u8 { - ChangePubkeyTx::TX_TYPE - } - - #[wasm_bindgen(js_name=getTx)] - pub fn get_tx(&mut self) -> JsValue { - serde_wasm_bindgen::to_value(&self.inner).unwrap() + pub fn get_inner_data(&self) -> Result { + Ok(serde_wasm_bindgen::to_value(&self.inner)?) } +} - #[wasm_bindgen] - pub fn sign(&mut self, signer: &mut ZklinkSigner) -> Result { - let msg = self.inner.get_bytes(); - Ok(signer.sign(&msg)?) +#[wasm_bindgen] +impl ChangePubKey { + pub fn get_inner_tx(&self) -> Result { + Ok(serde_wasm_bindgen::to_value(&self.inner)?) } - #[wasm_bindgen(js_name = getChangePubkeyMessage)] pub fn get_change_pubkey_message( &self, layer_one_chain_id: u32, verifying_contract: String, ) -> Result { - let contract = ZkLinkAddress::from_str(&verifying_contract).unwrap_or_default(); + let contract = ZkLinkAddress::from_str(&verifying_contract)?; let typed_data = self .inner .to_eip712_request_payload(layer_one_chain_id, &contract)?; Ok(typed_data.raw_data) } +} + +#[wasm_bindgen] +pub struct ChangePubKeyBuilder { + inner: TxChangePubKeyBuilder, +} - #[wasm_bindgen(js_name = setL2Signature)] - pub fn set_zklink_signature(&mut self, signature: String) -> Result<(), JsValue> { - self.inner.signature = ZkLinkSignature::from_hex(&signature)?; - Ok(()) +#[wasm_bindgen] +impl ChangePubKeyBuilder { + #[wasm_bindgen(constructor)] + pub fn new( + chain_id: u8, + account_id: u32, + sub_account_id: u8, + new_pubkey_hash: String, + fee_token: u16, + fee: String, + nonce: u32, + eth_signature: Option, + timestamp: u32, + ) -> Result { + let eth_signature = if let Some(s) = eth_signature { + Some(PackedEthSignature::from_hex(&s)?) + } else { + None + }; + let inner = TxChangePubKeyBuilder { + chain_id: chain_id.into(), + account_id: account_id.into(), + sub_account_id: sub_account_id.into(), + new_pubkey_hash: PubKeyHash::from_hex(&new_pubkey_hash)?, + fee_token: fee_token.into(), + fee: BigUint::from_str(&fee).unwrap(), + nonce: nonce.into(), + eth_signature, + timestamp: timestamp.into(), + }; + Ok(ChangePubKeyBuilder { inner }) } - #[wasm_bindgen(js_name = submitterSign)] - pub fn submitter_sign(&mut self, signer: &mut ZklinkSigner) -> Result { - let tx_hash = self.inner.tx_hash(); - Ok(signer.sign(&tx_hash)?) + #[wasm_bindgen] + pub fn build_change_pubkey(self) -> ChangePubKey { + ChangePubKey { + inner: ChangePubkeyTx::new(self.inner), + } } } + +#[wasm_bindgen(js_name=newChangePubkey)] +pub fn new_change_pubkey(builder: ChangePubKeyBuilder) -> ChangePubKey { + builder.build_change_pubkey() +} diff --git a/bindings/wasm/src/tx_types/transfer.rs b/bindings/wasm/src/tx_types/transfer.rs index 30fa635c..52ed4891 100644 --- a/bindings/wasm/src/tx_types/transfer.rs +++ b/bindings/wasm/src/tx_types/transfer.rs @@ -1,10 +1,9 @@ -use crate::crypto::ZklinkSigner; use std::str::FromStr; use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::JsValue; use zklink_sdk_types::basic_types::{AccountId, BigUint, TokenId, ZkLinkAddress}; use zklink_sdk_types::prelude::{Nonce, SubAccountId, TimeStamp, ZkLinkSignature}; -use zklink_sdk_types::tx_builder::TransferBuilder; +use zklink_sdk_types::tx_builder::TransferBuilder as TxTransferBuilder; use zklink_sdk_types::tx_type::transfer::Transfer as TransferTx; use zklink_sdk_types::tx_type::TxTrait; @@ -15,6 +14,18 @@ pub struct Transfer { #[wasm_bindgen] impl Transfer { + pub fn get_inner_tx(&self) -> Result { + Ok(serde_wasm_bindgen::to_value(&self.inner)?) + } +} + +#[wasm_bindgen] +pub struct TransferBuilder { + inner: TxTransferBuilder, +} + +#[wasm_bindgen] +impl TransferBuilder { #[wasm_bindgen(constructor)] pub fn new( account_id: u32, @@ -26,53 +37,30 @@ impl Transfer { amount: String, nonce: u32, ts: u32, - ) -> Result { - let transfer_builder = TransferBuilder { - account_id: AccountId(account_id), - to_address: ZkLinkAddress::from_str(&to_address).unwrap(), - from_sub_account_id: SubAccountId(from_sub_account_id), - to_sub_account_id: SubAccountId(to_sub_account_id), - token: TokenId(token), + ) -> Result { + let inner = TxTransferBuilder { + account_id: account_id.into(), + to_address: ZkLinkAddress::from_hex(&to_address)?, + from_sub_account_id: from_sub_account_id.into(), + to_sub_account_id: to_sub_account_id.into(), + token: token.into(), fee: BigUint::from_str(&fee).unwrap(), - nonce: Nonce(nonce), - timestamp: TimeStamp(ts), + nonce: nonce.into(), + timestamp: ts.into(), amount: BigUint::from_str(&amount).unwrap(), }; - Ok(Transfer { - inner: TransferTx::new(transfer_builder), - }) + Ok(TransferBuilder { inner }) } #[wasm_bindgen] - pub fn sign(&mut self, signer: &mut ZklinkSigner) -> Result { - let msg = self.inner.get_bytes(); - Ok(signer.sign(&msg)?) - } - - #[wasm_bindgen(js_name = getEthSignMessage)] - pub fn get_eth_sign_message(&self, token_symbol: String) -> String { - self.inner.get_eth_sign_msg(&token_symbol) - } - - #[wasm_bindgen(js_name=getTxType)] - pub fn get_tx_type(&self) -> u8 { - TransferTx::TX_TYPE - } - - #[wasm_bindgen(js_name=getTx)] - pub fn get_tx(&mut self) -> JsValue { - serde_wasm_bindgen::to_value(&self.inner).unwrap() - } - - #[wasm_bindgen(js_name = setL2Signature)] - pub fn set_zklink_signature(&mut self, signature: String) -> Result<(), JsValue> { - self.inner.signature = ZkLinkSignature::from_hex(&signature)?; - Ok(()) + pub fn build_transfer(self) -> Transfer { + Transfer { + inner: TransferTx::new(self.inner), + } } +} - #[wasm_bindgen(js_name = submitterSign)] - pub fn submitter_sign(&mut self, signer: &mut ZklinkSigner) -> Result { - let tx_hash = self.inner.tx_hash(); - Ok(signer.sign(&tx_hash)?) - } +#[wasm_bindgen(js_name=newTransfer)] +pub fn new_transfer(builder: TransferBuilder) -> Transfer { + builder.build_transfer() } diff --git a/bindings/wasm/tests/test_provider.rs b/bindings/wasm/tests/test_rpc.rs similarity index 100% rename from bindings/wasm/tests/test_provider.rs rename to bindings/wasm/tests/test_rpc.rs diff --git a/examples/Javascript/js-example/1_change_pubkey.js b/examples/Javascript/js-example/1_change_pubkey.js index 60cd6f2a..f3ee027a 100644 --- a/examples/Javascript/js-example/1_change_pubkey.js +++ b/examples/Javascript/js-example/1_change_pubkey.js @@ -1,59 +1,29 @@ import init, * as wasm from "./web-dist/zklink-sdk-web.js"; -async function getChangePubkeyData( - auth_type, - main_contract, - l1_client_id, - chain_id, - account_id, - sub_account_id, - new_pubkey_hash, - fee_token, - fee, - nonce, - ts, -) { - // var authData: wasm.; - // if (auth_type == 'OnChain') { - // - // } else if (auth_type == '') { - // - // } else { - // - // } - console.log("getChangePubkeyData"); - let tx = new wasm.ChangePubKey(chain_id, account_id, sub_account_id, new_pubkey_hash, fee_token, fee, nonce, null, ts); - let message = tx.getChangePubkeyMessage(l1_client_id,main_contract); - console.log(message); - return message; -} async function main() { await init(); const private_key = "be725250b123a39dab5b7579334d5888987c72a58f4508062545fe6e08ca94f4"; - const eth_signer = wasm.EthPrivateKeySigner.newFromHexPrivateKey(private_key); - const zklink_signer = wasm.ZklinkSigner.newFromEthSigner(private_key); const main_contract = "0x5505a8cD4594Dbf79d8C59C0Df1414AB871CA896"; const l1_client_id = 80001; const new_pubkey_hash = "0xd8d5fb6a6caef06aa3dc2abdcdc240987e5330fe"; const ts = Math.floor(Date.now() / 1000); - //auth type 'ECDSA' - let message = getChangePubkeyData('EthECDSA',main_contract,l1_client_id,1,10,1,new_pubkey_hash,18,"100000000000000",1,ts); - const msg = new TextEncoder().encode(message); try { - let signature = eth_signer.signMessage(msg); - console.log(signature); - let tx = new wasm.ChangePubKey(1, 10, 1, new_pubkey_hash, 18, "100000000000000", 1, signature, ts); - let tx_signature = tx.sign(zklink_signer); + let tx_builder = new wasm.ChangePubKeyBuilder( + 1,5,1,new_pubkey_hash,18,"100000000000000", + 1,"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b", + ts); + let tx = wasm.newChangePubkey(tx_builder); + const signer = new wasm.Signer(private_key); + //auth type 'ECDSA' + let tx_signature = signer.signChangePubkeyWithEthEcdsaAuth(tx,l1_client_id,main_contract); console.log(tx_signature); - tx.setL2Signature(tx_signature); - //send to zklink - let provder = new wasm.Provider("testnet"); - let signed_tx = new wasm.SignedTransaction(tx.getTxType(),tx.getTx()); - console.log(signed_tx); - let l1_signature = new wasm.TxL1Signature(wasm.L1SignatureType.Eth,signature); - let submitter_signature = tx.submitterSign(zklink_signer); + //auth type 'Create2' + //0x0000000000000000000000000000000000000000000000000000000000000000 + let submitter_signature = signer.SubmitterSignature(tx_signature.tx); console.log(submitter_signature); - let tx_hash = await provder.sendTransaction(signed_tx,l1_signature,submitter_signature); + //send to zklink + let rpc_client = new wasm.RpcClient("testnet"); + let tx_hash = await rpc_client.sendTransaction(tx_signature.tx,null,submitter_signature); console.log(tx_hash); } catch (error) { diff --git a/examples/Javascript/js-example/2_transfer.js b/examples/Javascript/js-example/2_transfer.js index e8a67e11..68f0c420 100644 --- a/examples/Javascript/js-example/2_transfer.js +++ b/examples/Javascript/js-example/2_transfer.js @@ -3,27 +3,20 @@ import init, * as wasm from "./web-dist/zklink-sdk-web.js"; async function main() { await init(); const private_key = "be725250b123a39dab5b7579334d5888987c72a58f4508062545fe6e08ca94f4"; - const eth_signer = wasm.EthPrivateKeySigner.newFromHexPrivateKey(private_key); - const zklink_signer = wasm.ZklinkSigner.newFromEthSigner(private_key); const to_address = "0x5505a8cD4594Dbf79d8C59C0Df1414AB871CA896"; const ts = Math.floor(Date.now() / 1000); - let tx = new wasm.Transfer(10, to_address, 1, 1, 18, "100000000000000", "10000000000000000", 1,ts); - let message = tx.getEthSignMessage("USDT"); - const msg = new TextEncoder().encode(message); try { - let signature = eth_signer.signMessage(msg); + let tx_builder = new wasm.TransferBuilder(10, to_address, 1, 1, 18, "100000000000000", "10000000000000000", 1,ts); + let transfer = wasm.newTransfer(tx_builder); + let signer = new wasm.Signer(private_key); + let signature = signer.signTransfer(transfer,"USDC") console.log(signature); - let tx_signature = tx.sign(zklink_signer); - console.log(tx_signature); - tx.setL2Signature(tx_signature); - //send to zklink - let provder = new wasm.Provider("testnet"); - let signed_tx = new wasm.SignedTransaction(tx.getTxType(),tx.getTx()); - console.log(signed_tx); - let l1_signature = new wasm.TxL1Signature(wasm.L1SignatureType.Eth,signature); - let submitter_signature = tx.submitterSign(zklink_signer); + + let submitter_signature = signer.submitterSignature(signature.tx); console.log(submitter_signature); - let tx_hash = await provder.sendTransaction(signed_tx,l1_signature,submitter_signature); + let rpc_client = new wasm.RpcClient("testnet"); + let l1_signature = new wasm.TxL1Signature(wasm.L1SignatureType.Eth,signature.eth_signature); + let tx_hash = await rpc_client.sendTransaction(signature.tx,l1_signature,submitter_signature); console.log(tx_hash); } catch (error) { diff --git a/examples/Javascript/js-example/index.html b/examples/Javascript/js-example/index.html index 44271a6f..fa5163ce 100644 --- a/examples/Javascript/js-example/index.html +++ b/examples/Javascript/js-example/index.html @@ -6,8 +6,9 @@ - - \ No newline at end of file diff --git a/interface/Cargo.toml b/interface/Cargo.toml index 56143676..b60a9596 100644 --- a/interface/Cargo.toml +++ b/interface/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] thiserror = "1.0" +wasm-bindgen = { version = "0.2.87", features = ["serde-serialize"] } zklink_sdk_signers = { path = "../signers" } zklink_sdk_types = { path = "../types" } diff --git a/interface/src/error.rs b/interface/src/error.rs index 12e6ccd1..3d4b12d9 100644 --- a/interface/src/error.rs +++ b/interface/src/error.rs @@ -1,4 +1,6 @@ use thiserror::Error; +#[cfg(target_arch = "wasm32")] +use wasm_bindgen::JsValue; use zklink_sdk_signers::eth_signer::error::EthSignerError; use zklink_sdk_signers::zklink_signer::error::ZkSignerError; @@ -11,3 +13,10 @@ pub enum SignError { #[error("Incorrect tx format")] IncorrectTx, } + +#[cfg(target_arch = "wasm32")] +impl From for JsValue { + fn from(error: SignError) -> Self { + JsValue::from_str(&format!("error: {error}")) + } +} diff --git a/provider/Cargo.toml b/provider/Cargo.toml index af31f504..ca388e4e 100644 --- a/provider/Cargo.toml +++ b/provider/Cargo.toml @@ -9,7 +9,6 @@ bigdecimal = { version = "0.3.0", features = ["serde"] } chrono = { version = "0.4", features = ["serde"] } reqwest = { version = "0.11", default-features = false, features = ["blocking", "json", "rustls-tls"] } serde = "1.0.137" -serde_json = "1.0" thiserror = "1.0" wasm-bindgen = { version = "0.2.87", features = ["serde-serialize"] } zklink_sdk_signers = { path = "../signers" } diff --git a/provider/src/error.rs b/provider/src/error.rs index ea73a27d..c676958d 100644 --- a/provider/src/error.rs +++ b/provider/src/error.rs @@ -18,9 +18,8 @@ pub enum RpcError { ParseJsonError, } - impl From for JsValue { fn from(error: RpcError) -> Self { JsValue::from_str(&format!("error: {error}")) } -} \ No newline at end of file +} diff --git a/signers/src/eth_signer/pk_signer.rs b/signers/src/eth_signer/pk_signer.rs index d48deaed..807f344a 100644 --- a/signers/src/eth_signer/pk_signer.rs +++ b/signers/src/eth_signer/pk_signer.rs @@ -7,9 +7,7 @@ use ethers::types::transaction::eip2718::TypedTransaction; use ethers::types::TxHash; use ethers::utils::hash_message; use k256::ecdsa::SigningKey; -use wasm_bindgen::prelude::wasm_bindgen; -#[wasm_bindgen] #[derive(Clone)] pub struct EthSigner { private_key: H256, diff --git a/types/Cargo.toml b/types/Cargo.toml index fe776dba..d07847ca 100644 --- a/types/Cargo.toml +++ b/types/Cargo.toml @@ -12,9 +12,9 @@ serde = "1.0.137" serde_json = "1.0.0" thiserror = "1.0" validator = { version = "0.15", features = ["derive"] } +wasm-bindgen = { version = "0.2.87", features = ["serde-serialize"] } zklink_sdk_signers = { path = "../signers" } zklink_sdk_utils = { path = "../utils" } -wasm-bindgen = { version = "0.2.87", features = ["serde-serialize"] } [dev-dependencies] diff --git a/types/src/error.rs b/types/src/error.rs index 6d72a5a6..bc448a42 100644 --- a/types/src/error.rs +++ b/types/src/error.rs @@ -1,4 +1,6 @@ use thiserror::Error; +#[cfg(target_arch = "wasm32")] +use wasm_bindgen::JsValue; #[derive(Debug, Error, PartialEq)] pub enum TypeError { @@ -15,3 +17,10 @@ pub enum TypeError { #[error("Integer is too big")] TooBigInteger, } + +#[cfg(target_arch = "wasm32")] +impl From for JsValue { + fn from(error: TypeError) -> Self { + JsValue::from_str(&format!("error: {error}")) + } +} From d1e1b71c4d66bf602bea45d44760b729b4b45249 Mon Sep 17 00:00:00 2001 From: nickwest Date: Wed, 11 Oct 2023 06:51:22 -0700 Subject: [PATCH 5/6] Add create2 auth for change_pubkey example and fix review suggests --- bindings/wasm/src/rpc_client.rs | 22 ++--- bindings/wasm/src/rpc_type_converter.rs | 56 +++++------ bindings/wasm/src/signer.rs | 12 +-- bindings/wasm/src/tx_types/change_pubkey.rs | 15 ++- bindings/wasm/src/tx_types/transfer.rs | 8 +- bindings/wasm/tests/test_rpc.rs | 65 ++++++------- .../Javascript/js-example/1_change_pubkey.js | 45 +++++++-- examples/Javascript/js-example/index.html | 4 +- examples/Javascript/js-example/rpc.js | 94 +++++++++---------- interface/src/sign_change_pubkey.rs | 49 +++++++++- interface/src/signer.rs | 9 +- provider/src/network.rs | 4 + provider/tests/test_rpc.rs | 77 +++++++++++++++ signers/src/eth_signer/error.rs | 9 ++ signers/src/eth_signer/mod.rs | 2 - signers/src/eth_signer/wasm_binding.rs | 8 -- signers/src/zklink_signer/error.rs | 9 ++ signers/src/zklink_signer/mod.rs | 2 - signers/src/zklink_signer/wasm_binding.rs | 8 -- 19 files changed, 330 insertions(+), 168 deletions(-) create mode 100644 provider/tests/test_rpc.rs delete mode 100644 signers/src/eth_signer/wasm_binding.rs delete mode 100644 signers/src/zklink_signer/wasm_binding.rs diff --git a/bindings/wasm/src/rpc_client.rs b/bindings/wasm/src/rpc_client.rs index 99ad24d6..0f27cd3d 100644 --- a/bindings/wasm/src/rpc_client.rs +++ b/bindings/wasm/src/rpc_client.rs @@ -1,4 +1,4 @@ -use crate::rpc_type_converter::{AccountQueryParam, SignedTransaction, TxL1Signature}; +use crate::rpc_type_converter::{AccountQuery, TxLayer1Signature}; use getrandom::getrandom; use jsonrpsee::core::params::ArrayParams; use jsonrpsee::core::traits::ToRpcParams; @@ -11,16 +11,16 @@ use wasm_bindgen::JsValue; use zklink_sdk_provider::error::RpcError; use zklink_sdk_provider::network::Network; use zklink_sdk_provider::response::{ - AccountInfoResp, AccountQuery, AccountSnapshotResp, BlockNumberResp, BlockOnChainResp, - BlockResp, ChainResp, FastWithdrawTxResp, ForwardTxResp, Page, SubAccountBalances, - SubAccountOrders, TokenResp, TxHashOrDetailResp, TxResp, ZkLinkTxHistory, + AccountInfoResp, AccountQuery as RpcAccountQuery, AccountSnapshotResp, BlockNumberResp, + BlockOnChainResp, BlockResp, ChainResp, FastWithdrawTxResp, ForwardTxResp, Page, + SubAccountBalances, SubAccountOrders, TokenResp, TxHashOrDetailResp, TxResp, ZkLinkTxHistory, }; use zklink_sdk_signers::zklink_signer::ZkLinkSignature; use zklink_sdk_types::basic_types::bigunit_wrapper::BigUintSerdeWrapper; use zklink_sdk_types::basic_types::tx_hash::TxHash; use zklink_sdk_types::basic_types::{AccountId, BlockNumber, ChainId, SubAccountId, TokenId}; use zklink_sdk_types::prelude::ZkLinkAddress; -use zklink_sdk_types::signatures::TxLayer1Signature; +use zklink_sdk_types::signatures::TxLayer1Signature as TypesTxLayer1Signature; use zklink_sdk_types::tx_type::zklink_tx::ZkLinkTx; use zklink_sdk_types::tx_type::zklink_tx::ZkLinkTxType; @@ -90,12 +90,12 @@ impl RpcClient { #[wasm_bindgen(js_name=getAccountSnapshot)] pub async fn account_query( &self, - account_query: AccountQueryParam, + account_query: AccountQuery, sub_account_id: Option, block_number: Option, ) -> Result { let mut builder = ArrayParams::new(); - let _ = builder.insert(AccountQuery::from(account_query)); + let _ = builder.insert(RpcAccountQuery::from(account_query)); let _ = builder.insert(sub_account_id.map(|id| SubAccountId(id))); let _ = builder.insert(block_number.map(|number| BlockNumber(number))); rpc_request!( @@ -110,13 +110,13 @@ impl RpcClient { pub async fn send_transaction( &self, tx: JsValue, - l1_signature: Option, + l1_signature: Option, l2_signature: Option, ) -> Result { let mut builder = ArrayParams::new(); let zklink_tx: ZkLinkTx = serde_wasm_bindgen::from_value(tx)?; let _ = builder.insert(zklink_tx); - let _ = builder.insert(l1_signature.map(|t| TxLayer1Signature::from(t))); + let _ = builder.insert(l1_signature.map(|t| TypesTxLayer1Signature::from(t))); let _ = builder.insert(l2_signature.map(|s| ZkLinkSignature::from_hex(&s).unwrap())); rpc_request!("sendTransaction", builder, &self.server_url, TxHash) } @@ -191,9 +191,9 @@ impl RpcClient { } #[wasm_bindgen(js_name=getAccount)] - pub async fn account_info(&self, account_query: AccountQueryParam) -> Result { + pub async fn account_info(&self, account_query: AccountQuery) -> Result { let mut builder = ArrayParams::new(); - let _ = builder.insert(AccountQuery::from(account_query)); + let _ = builder.insert(RpcAccountQuery::from(account_query)); rpc_request!("getAccount", builder, &self.server_url, AccountInfoResp) } diff --git a/bindings/wasm/src/rpc_type_converter.rs b/bindings/wasm/src/rpc_type_converter.rs index 707ec35c..651cf904 100644 --- a/bindings/wasm/src/rpc_type_converter.rs +++ b/bindings/wasm/src/rpc_type_converter.rs @@ -1,15 +1,15 @@ use std::str::FromStr; use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::JsValue; -use zklink_sdk_provider::response::AccountQuery; +use zklink_sdk_provider::response::AccountQuery as RpcAccountQuery; use zklink_sdk_signers::eth_signer::{EIP1271Signature, PackedEthSignature}; use zklink_sdk_signers::starknet_signer::StarkECDSASignature; use zklink_sdk_types::basic_types::AccountId; use zklink_sdk_types::prelude::ZkLinkAddress; -use zklink_sdk_types::signatures::TxLayer1Signature; +use zklink_sdk_types::signatures::TxLayer1Signature as TypesTxLayer1Signature; use zklink_sdk_types::tx_type::change_pubkey::ChangePubKey; use zklink_sdk_types::tx_type::transfer::Transfer; -use zklink_sdk_types::tx_type::zklink_tx::ZkLinkTx; +use zklink_sdk_types::tx_type::zklink_tx::ZkLinkTx as TypesZkLinkTx; #[wasm_bindgen] #[derive(Copy, Clone)] @@ -27,68 +27,68 @@ pub enum L1SignatureType { } #[wasm_bindgen] -pub struct AccountQueryParam { +pub struct AccountQuery { query_type: AccountQueryType, query_param: String, } #[wasm_bindgen] -pub struct TxL1Signature { +pub struct TxLayer1Signature { sign_type: L1SignatureType, signature: String, } #[wasm_bindgen] -pub struct SignedTransaction { +pub struct ZkLinkTx { tx_type: u8, tx: JsValue, } #[wasm_bindgen] -impl AccountQueryParam { +impl AccountQuery { #[wasm_bindgen(constructor)] - pub fn new(query_type: AccountQueryType, query_param: String) -> AccountQueryParam { - AccountQueryParam { + pub fn new(query_type: AccountQueryType, query_param: String) -> AccountQuery { + AccountQuery { query_type, query_param, } } } -impl From for AccountQuery { - fn from(query: AccountQueryParam) -> AccountQuery { +impl From for RpcAccountQuery { + fn from(query: AccountQuery) -> RpcAccountQuery { match query.query_type { AccountQueryType::AccountId => { - AccountQuery::Id(AccountId(query.query_param.parse::().unwrap())) + RpcAccountQuery::Id(AccountId(query.query_param.parse::().unwrap())) } AccountQueryType::Address => { - AccountQuery::Address(ZkLinkAddress::from_str(&query.query_param).unwrap()) + RpcAccountQuery::Address(ZkLinkAddress::from_str(&query.query_param).unwrap()) } } } } #[wasm_bindgen] -impl TxL1Signature { +impl TxLayer1Signature { #[wasm_bindgen] - pub fn new(sign_type: L1SignatureType, signature: String) -> TxL1Signature { - TxL1Signature { + pub fn new(sign_type: L1SignatureType, signature: String) -> TxLayer1Signature { + TxLayer1Signature { sign_type, signature, } } } -impl From for TxLayer1Signature { - fn from(signature: TxL1Signature) -> TxLayer1Signature { +impl From for TypesTxLayer1Signature { + fn from(signature: TxLayer1Signature) -> TypesTxLayer1Signature { match signature.sign_type { - L1SignatureType::Eth => TxLayer1Signature::EthereumSignature( + L1SignatureType::Eth => TypesTxLayer1Signature::EthereumSignature( PackedEthSignature::from_hex(&signature.signature).unwrap(), ), - L1SignatureType::Eip1271 => TxLayer1Signature::EIP1271Signature(EIP1271Signature( + L1SignatureType::Eip1271 => TypesTxLayer1Signature::EIP1271Signature(EIP1271Signature( hex::decode(signature.signature).unwrap(), )), - L1SignatureType::Stark => TxLayer1Signature::StarkSignature(StarkECDSASignature( + L1SignatureType::Stark => TypesTxLayer1Signature::StarkSignature(StarkECDSASignature( hex::decode(signature.signature).unwrap(), )), } @@ -96,23 +96,23 @@ impl From for TxLayer1Signature { } #[wasm_bindgen] -impl SignedTransaction { +impl ZkLinkTx { #[wasm_bindgen(constructor)] - pub fn new(tx_type: u8, tx: JsValue) -> SignedTransaction { - SignedTransaction { tx_type, tx } + pub fn new(tx_type: u8, tx: JsValue) -> ZkLinkTx { + ZkLinkTx { tx_type, tx } } } -impl From for ZkLinkTx { - fn from(tx: SignedTransaction) -> ZkLinkTx { +impl From for TypesZkLinkTx { + fn from(tx: ZkLinkTx) -> TypesZkLinkTx { match tx.tx_type { ChangePubKey::TX_TYPE => { let change_pubkey: ChangePubKey = serde_wasm_bindgen::from_value(tx.tx).unwrap(); - ZkLinkTx::ChangePubKey(Box::new(change_pubkey)) + TypesZkLinkTx::ChangePubKey(Box::new(change_pubkey)) } Transfer::TX_TYPE => { let transfer: Transfer = serde_wasm_bindgen::from_value(tx.tx).unwrap(); - ZkLinkTx::Transfer(Box::new(transfer)) + TypesZkLinkTx::Transfer(Box::new(transfer)) } _ => { panic!("Not support tx type!") diff --git a/bindings/wasm/src/signer.rs b/bindings/wasm/src/signer.rs index 0d02dbec..9172d387 100644 --- a/bindings/wasm/src/signer.rs +++ b/bindings/wasm/src/signer.rs @@ -1,11 +1,8 @@ use crate::tx_types::change_pubkey::{ChangePubKey, Create2Data}; use crate::tx_types::transfer::Transfer; -use std::str::FromStr; use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::JsValue; use zklink_sdk_interface::signer::Signer as InterfaceSigner; -use zklink_sdk_signers::eth_signer::EthSigner; -use zklink_sdk_signers::zklink_signer::ZkLinkSigner; use zklink_sdk_types::basic_types::ZkLinkAddress; use zklink_sdk_types::tx_type::change_pubkey::ChangePubKey as TxChangePubKey; use zklink_sdk_types::tx_type::change_pubkey::Create2Data as ChangePubKeyCreate2Data; @@ -48,14 +45,17 @@ impl Signer { &self, tx: ChangePubKey, create2_data: Create2Data, + from_account: String, ) -> Result { let inner_tx = tx.get_inner_tx()?; let change_pubkey: TxChangePubKey = serde_wasm_bindgen::from_value(inner_tx)?; let inner_data = create2_data.get_inner_data()?; let create2_data: ChangePubKeyCreate2Data = serde_wasm_bindgen::from_value(inner_data)?; - let signature = self - .inner - .sign_change_pubkey_with_create2data_auth(change_pubkey, create2_data)?; + let signature = self.inner.sign_change_pubkey_with_create2data_auth( + change_pubkey, + create2_data, + ZkLinkAddress::from_hex(&from_account)?, + )?; Ok(serde_wasm_bindgen::to_value(&signature)?) } diff --git a/bindings/wasm/src/tx_types/change_pubkey.rs b/bindings/wasm/src/tx_types/change_pubkey.rs index d7d617f9..49124548 100644 --- a/bindings/wasm/src/tx_types/change_pubkey.rs +++ b/bindings/wasm/src/tx_types/change_pubkey.rs @@ -3,14 +3,13 @@ use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::JsValue; use zklink_sdk_signers::eth_signer::packed_eth_signature::PackedEthSignature; use zklink_sdk_signers::zklink_signer::pubkey_hash::PubKeyHash; -use zklink_sdk_types::basic_types::{AccountId, BigUint, TokenId, ZkLinkAddress}; +use zklink_sdk_types::basic_types::{BigUint, ZkLinkAddress}; use zklink_sdk_types::error::TypeError; -use zklink_sdk_types::prelude::{ChainId, Nonce, SubAccountId, TimeStamp, ZkLinkSignature, H256}; +use zklink_sdk_types::prelude::H256; use zklink_sdk_types::tx_builder::ChangePubKeyBuilder as TxChangePubKeyBuilder; use zklink_sdk_types::tx_type::change_pubkey::{ ChangePubKey as ChangePubkeyTx, Create2Data as ChangePubKeyCreate2Data, }; -use zklink_sdk_types::tx_type::TxTrait; #[wasm_bindgen] pub enum EthAuthType { @@ -45,6 +44,12 @@ impl Create2Data { }) } + #[wasm_bindgen] + pub fn salt(&self, pubkey_hash: &str) -> String { + let salt_bytes = self.inner.salt(pubkey_hash.as_bytes()); + hex::encode(salt_bytes) + } + pub fn get_inner_data(&self) -> Result { Ok(serde_wasm_bindgen::to_value(&self.inner)?) } @@ -108,7 +113,7 @@ impl ChangePubKeyBuilder { } #[wasm_bindgen] - pub fn build_change_pubkey(self) -> ChangePubKey { + pub fn build(self) -> ChangePubKey { ChangePubKey { inner: ChangePubkeyTx::new(self.inner), } @@ -117,5 +122,5 @@ impl ChangePubKeyBuilder { #[wasm_bindgen(js_name=newChangePubkey)] pub fn new_change_pubkey(builder: ChangePubKeyBuilder) -> ChangePubKey { - builder.build_change_pubkey() + builder.build() } diff --git a/bindings/wasm/src/tx_types/transfer.rs b/bindings/wasm/src/tx_types/transfer.rs index 52ed4891..4b24c00a 100644 --- a/bindings/wasm/src/tx_types/transfer.rs +++ b/bindings/wasm/src/tx_types/transfer.rs @@ -1,11 +1,9 @@ use std::str::FromStr; use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::JsValue; -use zklink_sdk_types::basic_types::{AccountId, BigUint, TokenId, ZkLinkAddress}; -use zklink_sdk_types::prelude::{Nonce, SubAccountId, TimeStamp, ZkLinkSignature}; +use zklink_sdk_types::basic_types::{BigUint, ZkLinkAddress}; use zklink_sdk_types::tx_builder::TransferBuilder as TxTransferBuilder; use zklink_sdk_types::tx_type::transfer::Transfer as TransferTx; -use zklink_sdk_types::tx_type::TxTrait; #[wasm_bindgen] pub struct Transfer { @@ -53,7 +51,7 @@ impl TransferBuilder { } #[wasm_bindgen] - pub fn build_transfer(self) -> Transfer { + pub fn build(self) -> Transfer { Transfer { inner: TransferTx::new(self.inner), } @@ -62,5 +60,5 @@ impl TransferBuilder { #[wasm_bindgen(js_name=newTransfer)] pub fn new_transfer(builder: TransferBuilder) -> Transfer { - builder.build_transfer() + builder.build() } diff --git a/bindings/wasm/tests/test_rpc.rs b/bindings/wasm/tests/test_rpc.rs index d3966fcd..5d441756 100644 --- a/bindings/wasm/tests/test_rpc.rs +++ b/bindings/wasm/tests/test_rpc.rs @@ -3,48 +3,46 @@ use std::str::FromStr; use wasm_bindgen::JsValue; use wasm_bindgen_test::wasm_bindgen_test; use wasm_bindgen_test::wasm_bindgen_test_configure; -use zklink_sdk_provider::rpc_wasm::WasmRpcClient; use zklink_sdk_signers::eth_signer::EthSigner; use zklink_sdk_signers::zklink_signer::{PubKeyHash, ZkLinkSigner}; use zklink_sdk_types::basic_types::{AccountId, BigUint, ChainId, Nonce, TokenId, ZkLinkAddress}; use zklink_sdk_types::prelude::{SubAccountId, TimeStamp}; use zklink_sdk_types::tx_builder::ChangePubKeyBuilder; use zklink_sdk_types::tx_type::change_pubkey::ChangePubKey; -use zklink_sdk_types::tx_type::zklink_tx::ZkLinkTx; +use zklink_sdk_types::tx_type::zklink_tx::ZkLinkTx as TypesZkLinkTx; use zklink_sdk_types::tx_type::ZkSignatureTrait; -use zklink_sdk_wasm::rpc_client::{Provider, RpcClient}; -use zklink_sdk_wasm::rpc_type_converter::{AccountQueryParam, AccountQueryType, SignedTransaction}; +use zklink_sdk_wasm::rpc_client::RpcClient; +use zklink_sdk_wasm::rpc_type_converter::{AccountQuery, AccountQueryType, ZkLinkTx}; wasm_bindgen_test_configure!(run_in_worker); -#[wasm_bindgen_test] -async fn test_get_tokens() { - web_sys::console::log_1(&JsValue::from_str("123")); - let client = WasmRpcClient::new("https://api-v1.zk.link".to_owned()); - let ret = client.tokens().await; - if let Err(e) = ret { - web_sys::console::log_1(&JsValue::from_str(&format!("{:?}", e))); - } else { - web_sys::console::log_1(&JsValue::from_str(&format!("{:?}", ret.unwrap()))); - } - // assert!(ret.is_err()); -} - -#[wasm_bindgen_test] -async fn test_account_query() { - let provider = Provider::new("testnet"); - let account_id = AccountQueryParam::new(AccountQueryType::AccountId, "5".to_string()); - let account_resp = provider.account_query(account_id.into(), None, None).await; - if let Err(e) = account_resp { - web_sys::console::log_1(&JsValue::from_str(&format!("{:?}", e))); - } else { - web_sys::console::log_1(&JsValue::from_str(&format!("{:?}", account_resp.unwrap()))); - } -} +// #[wasm_bindgen_test] +// async fn test_get_tokens() { +// let client = RpcClient::new("testnet"); +// let ret = client.tokens().await; +// if let Err(e) = ret { +// web_sys::console::log_1(&JsValue::from_str(&format!("{:?}", e))); +// } else { +// web_sys::console::log_1(&JsValue::from_str(&format!("{:?}", ret.unwrap()))); +// } +// // assert!(ret.is_err()); +// } +// +// #[wasm_bindgen_test] +// async fn test_account_query() { +// let client = RpcClient::new("testnet"); +// let account_id = AccountQuery::new(AccountQueryType::AccountId, "5".to_string()); +// let account_resp = client.account_query(account_id.into(), None, None).await; +// if let Err(e) = account_resp { +// web_sys::console::log_1(&JsValue::from_str(&format!("{:?}", e))); +// } else { +// web_sys::console::log_1(&JsValue::from_str(&format!("{:?}", account_resp.unwrap()))); +// } +// } #[wasm_bindgen_test] async fn test_send_change_pubkey() { web_sys::console::log_1(&JsValue::from_str("123")); - let client = RpcClient::new("testnet"); + let client = RpcClient::new("devnet"); let private_key = "be725250b123a39dab5b7579334d5888987c72a58f4508062545fe6e08ca94f4"; let eth_signer = EthSigner::try_from(private_key).unwrap(); let zklink_signer = ZkLinkSigner::new_from_hex_eth_signer(private_key).unwrap(); @@ -87,16 +85,13 @@ async fn test_send_change_pubkey() { }; let mut tx = ChangePubKey::new(builder_with_sig); tx.sign(&zklink_signer).unwrap(); - let submitter_signature = tx.signature.as_hex(); + let submitter_signature = tx.submitter_signature(&zklink_signer).unwrap(); //send to zklink let ret = client .send_transaction( - SignedTransaction::new( - ChangePubKey::TX_TYPE, - serde_wasm_bindgen::to_value(&tx).unwrap(), - ), + serde_wasm_bindgen::to_value(&TypesZkLinkTx::ChangePubKey(Box::new(tx))).unwrap(), None, - Some(l2_signature), + Some(submitter_signature.as_hex()), ) .await; if let Err(e) = ret { diff --git a/examples/Javascript/js-example/1_change_pubkey.js b/examples/Javascript/js-example/1_change_pubkey.js index f3ee027a..c53a27e3 100644 --- a/examples/Javascript/js-example/1_change_pubkey.js +++ b/examples/Javascript/js-example/1_change_pubkey.js @@ -1,6 +1,6 @@ import init, * as wasm from "./web-dist/zklink-sdk-web.js"; -async function main() { +async function testEcdsaAuth() { await init(); const private_key = "be725250b123a39dab5b7579334d5888987c72a58f4508062545fe6e08ca94f4"; const main_contract = "0x5505a8cD4594Dbf79d8C59C0Df1414AB871CA896"; @@ -14,15 +14,47 @@ async function main() { ts); let tx = wasm.newChangePubkey(tx_builder); const signer = new wasm.Signer(private_key); - //auth type 'ECDSA' let tx_signature = signer.signChangePubkeyWithEthEcdsaAuth(tx,l1_client_id,main_contract); console.log(tx_signature); + + let submitter_signature = signer.submitterSignature(tx_signature.tx); + console.log(submitter_signature); + //send to zklink + let rpc_client = new wasm.RpcClient("devnet"); + let tx_hash = await rpc_client.sendTransaction(tx_signature.tx,null,submitter_signature); + console.log(tx_hash); + + } catch (error) { + console.error(error); + } + +} + +async function testCreate2() { + await init(); + const private_key = "43be0b8bdeccb5a13741c8fd076bf2619bfc9f6dcc43ad6cf965ab489e156ced"; + const new_pubkey_hash = "0xd8d5fb6a6caef06aa3dc2abdcdc240987e5330fe"; + const ts = Math.floor(Date.now() / 1000); + try { + let tx_builder = new wasm.ChangePubKeyBuilder( + 1,5,1,new_pubkey_hash,18,"100000000000000", + 1,"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b", + ts); + let tx = wasm.newChangePubkey(tx_builder); + const signer = new wasm.Signer(private_key); //auth type 'Create2' - //0x0000000000000000000000000000000000000000000000000000000000000000 - let submitter_signature = signer.SubmitterSignature(tx_signature.tx); + const creator_address = "0x6E253C951A40fAf4032faFbEc19262Cd1531A5F5"; + const salt = "0x0000000000000000000000000000000000000000000000000000000000000000"; + const code_hash = "0x4f063cd4b2e3a885f61fefb0988cc12487182c4f09ff5de374103f5812f33fe7"; + let create2_data = new wasm.Create2Data(creator_address,salt,code_hash); + let from_account = "0x4504d5BE8634e3896d42784A5aB89fc41C3d4511"; + let tx_signature = signer.signChangePubkeyWithCreate2DataAuth(tx,create2_data,from_account); + console.log(tx_signature); + + let submitter_signature = signer.submitterSignature(tx_signature.tx); console.log(submitter_signature); //send to zklink - let rpc_client = new wasm.RpcClient("testnet"); + let rpc_client = new wasm.RpcClient("devnet"); let tx_hash = await rpc_client.sendTransaction(tx_signature.tx,null,submitter_signature); console.log(tx_hash); @@ -32,4 +64,5 @@ async function main() { } -main(); +await testEcdsaAuth(); +await testCreate2(); diff --git a/examples/Javascript/js-example/index.html b/examples/Javascript/js-example/index.html index fa5163ce..2de4a22b 100644 --- a/examples/Javascript/js-example/index.html +++ b/examples/Javascript/js-example/index.html @@ -6,9 +6,9 @@ - + \ No newline at end of file diff --git a/examples/Javascript/js-example/rpc.js b/examples/Javascript/js-example/rpc.js index 19702a3c..bad186aa 100644 --- a/examples/Javascript/js-example/rpc.js +++ b/examples/Javascript/js-example/rpc.js @@ -3,57 +3,57 @@ async function main() { await init(); try { let client = new wasm.RpcClient("mainet"); - // // 1.getSupportTokens - // let tokens = await provider.getSupportTokens(); - // console.log(tokens); - // // 2.getAccountSnapshot - // let account_id = new wasm.AccountQueryParam(wasm.AccountQueryType.AccountId, "5"); - // let sub_account_id = 1; - // // let block_number = 100; - // let account_resp = await provider.getAccountSnapshot(account_id,sub_account_id,null); - // console.log(account_resp); - // // 3.sendTransaction(test on the tx example) + // 1.getSupportTokens + let tokens = await client.getSupportTokens(); + console.log(tokens); + // 2.getAccountSnapshot + let account_id = new wasm.AccountQuery(wasm.AccountQueryType.AccountId, "5"); + let sub_account_id = 1; + // let block_number = 100; + let account_resp = await client.getAccountSnapshot(account_id,sub_account_id,null); + console.log(account_resp); + // 3.sendTransaction(test on the tx example) // 4.getSupportChains let chains = await client.getSupportChains(); console.log(chains); console.log(chains.length) - // // 5.getLatestBlockNumber - // let block_info = await provider.getLatestBlockNumber(); - // console.log(block_info); - // // 6.getBlockByNumber - // let block_detail = await provider.getBlockByNumber(100,true,true); - // console.log(block_detail); - // // 7.getPendingBlock - // let pending_block_info = await provider.getPendingBlock(1696743981000n,true,true,null); - // console.log(pending_block_info); - // // 8.getBlockOnChainByNumber - // let on_chain_block_info = await provider.getBlockOnChainByNumber(100); - // console.log(on_chain_block_info); - // // 9.getAccount - // let get_account_id = new wasm.AccountQueryParam(wasm.AccountQueryType.AccountId, "10"); - // let account = await provider.getAccount(get_account_id); - // console.log(account); - // // 10.getAccountBalances - // let balances = await provider.getAccountBalances(20,1); - // console.log(balances); - // // 11.getAccountOrderSlots - // let slots = await provider.getAccountOrderSlots(20,1); - // console.log(slots); - // // 12.getTokenReserve - // let reserve = await provider.getTokenReserve(18,false); - // console.log(reserve); - // // 13.getTransactionByHash - // let tx_hash = "0x0cbeabac1a2257fb095c2465e148570e32793345442b39bf64cad4ed87475f9b"; - // let tx_info = await provider.getTransactionByHash(tx_hash,false); - // console.log(tx_info); - // // 14.getAccountTransactionHistory - // let history = await provider.getAccountTransactionHistory(wasm.L2TxType.Deposit,"0x12aFF993702B5d623977A9044686Fa1A2B0c2147",0n,5); - // console.log(history); - // // 15.getFastWithdrawTxs - // let fast_withdraw_txs = await provider.getFastWithdrawTxs(1696743981000n,10); - // console.log(fast_withdraw_txs); - // // 16.pullForwardTxs - // // 17.confirmFullExit + // 5.getLatestBlockNumber + let block_info = await client.getLatestBlockNumber(); + console.log(block_info); + // 6.getBlockByNumber + let block_detail = await client.getBlockByNumber(100,true,true); + console.log(block_detail); + // 7.getPendingBlock + let pending_block_info = await client.getPendingBlock(1696743981000n,true,true,null); + console.log(pending_block_info); + // 8.getBlockOnChainByNumber + let on_chain_block_info = await client.getBlockOnChainByNumber(100); + console.log(on_chain_block_info); + // 9.getAccount + let get_account_id = new wasm.AccountQuery(wasm.AccountQueryType.AccountId, "10"); + let account = await client.getAccount(get_account_id); + console.log(account); + // 10.getAccountBalances + let balances = await client.getAccountBalances(20,1); + console.log(balances); + // 11.getAccountOrderSlots + let slots = await client.getAccountOrderSlots(20,1); + console.log(slots); + // 12.getTokenReserve + let reserve = await client.getTokenReserve(18,false); + console.log(reserve); + // 13.getTransactionByHash + let tx_hash = "0x0cbeabac1a2257fb095c2465e148570e32793345442b39bf64cad4ed87475f9b"; + let tx_info = await client.getTransactionByHash(tx_hash,false); + console.log(tx_info); + // 14.getAccountTransactionHistory + let history = await client.getAccountTransactionHistory(wasm.ZkLinkTxType.Deposit,"0x12aFF993702B5d623977A9044686Fa1A2B0c2147",0n,5); + console.log(history); + // 15.getFastWithdrawTxs + let fast_withdraw_txs = await client.getFastWithdrawTxs(1696743981000n,10); + console.log(fast_withdraw_txs); + // 16.pullForwardTxs + // 17.confirmFullExit } catch (error) { console.error(error); } diff --git a/interface/src/sign_change_pubkey.rs b/interface/src/sign_change_pubkey.rs index 07636d5a..077c5b63 100644 --- a/interface/src/sign_change_pubkey.rs +++ b/interface/src/sign_change_pubkey.rs @@ -10,7 +10,6 @@ use zklink_sdk_signers::zklink_signer::pk_signer::ZkLinkSigner; use zklink_sdk_types::basic_types::ZkLinkAddress; #[cfg(not(feature = "ffi"))] use zklink_sdk_types::prelude::TxSignature; -#[cfg(feature = "ffi")] use zklink_sdk_types::tx_type::change_pubkey::Create2Data; use zklink_sdk_types::tx_type::change_pubkey::{ChangePubKey, ChangePubKeyAuthData}; use zklink_sdk_types::tx_type::TxTrait; @@ -78,6 +77,22 @@ pub fn check_create2data( } } +#[cfg(not(feature = "ffi"))] +pub fn check_create2data( + zklink_singer: &ZkLinkSigner, + data: Create2Data, + account_address: ZkLinkAddress, +) -> Result<(), SignError> { + let pubkey_hash = zklink_singer.public_key().public_key_hash(); + let from_address = data.get_address(&pubkey_hash.data); + println!("{:?} {:?}", from_address, account_address); + if from_address.as_bytes() != account_address.as_bytes() { + Err(SignError::IncorrectTx) + } else { + Ok(()) + } +} + #[cfg(feature = "ffi")] pub fn create_signed_change_pubkey( zklink_singer: Arc, @@ -89,3 +104,35 @@ pub fn create_signed_change_pubkey( tx.signature = zklink_singer.sign_musig(&tx.get_bytes())?; Ok(Arc::new(tx)) } + +#[cfg(test)] +mod test { + use super::*; + use std::str::FromStr; + use zklink_sdk_types::prelude::H256; + + #[test] + fn test_check_create2() { + let creator_address = + ZkLinkAddress::from_hex("0x6E253C951A40fAf4032faFbEc19262Cd1531A5F5").unwrap(); + let salt_arg = + H256::from_str("0x0000000000000000000000000000000000000000000000000000000000000000") + .unwrap(); + let code_hash = + H256::from_str("0x4f063cd4b2e3a885f61fefb0988cc12487182c4f09ff5de374103f5812f33fe7") + .unwrap(); + let create2_data = Create2Data { + creator_address, + code_hash, + salt_arg, + }; + let from_account = + ZkLinkAddress::from_hex("0x4504d5BE8634e3896d42784A5aB89fc41C3d4511").unwrap(); + let eth_private_key = "43be0b8bdeccb5a13741c8fd076bf2619bfc9f6dcc43ad6cf965ab489e156ced"; + let zk_signer = ZkLinkSigner::new_from_hex_eth_signer(eth_private_key).unwrap(); + + if let Err(e) = check_create2data(&zk_signer, create2_data, from_account) { + println!("{:?}", e) + } + } +} diff --git a/interface/src/signer.rs b/interface/src/signer.rs index e75c70db..2babbaa7 100644 --- a/interface/src/signer.rs +++ b/interface/src/signer.rs @@ -5,6 +5,7 @@ use crate::sign_transfer::sign_transfer; use crate::sign_withdraw::sign_withdraw; use zklink_sdk_types::prelude::TxSignature; +use crate::sign_change_pubkey::check_create2data; #[cfg(feature = "ffi")] use std::sync::Arc; use zklink_sdk_signers::eth_signer::error::EthSignerError; @@ -40,7 +41,9 @@ impl Signer { &self, mut tx: ChangePubKey, create2data: Create2Data, + from_account: ZkLinkAddress, ) -> Result { + check_create2data(&self.zklink_signer, create2data.clone(), from_account)?; tx.sign(&self.zklink_signer)?; let should_valid = tx.is_signature_valid(); assert!(should_valid); @@ -59,9 +62,10 @@ impl Signer { &self, tx: Arc, create2data: Create2Data, + from_account: ZkLinkAddress, ) -> Result { let tx = (*tx).clone(); - self.do_sign_change_pubkey_with_create2data_auth(tx, create2data) + self.do_sign_change_pubkey_with_create2data_auth(tx, create2data, from_account) } #[cfg(not(feature = "ffi"))] @@ -70,8 +74,9 @@ impl Signer { &self, tx: ChangePubKey, create2data: Create2Data, + from_account: ZkLinkAddress, ) -> Result { - self.do_sign_change_pubkey_with_create2data_auth(tx, create2data) + self.do_sign_change_pubkey_with_create2data_auth(tx, create2data, from_account) } #[cfg(feature = "ffi")] diff --git a/provider/src/network.rs b/provider/src/network.rs index e6d6ac62..f5379805 100644 --- a/provider/src/network.rs +++ b/provider/src/network.rs @@ -10,6 +10,8 @@ pub enum Network { MainNet, /// Test network for testkit purposes TestNet, + /// Develop network + DevNet, } impl Network { @@ -17,6 +19,7 @@ impl Network { match self { Network::MainNet => "https://api-v1.zk.link", Network::TestNet => "https://aws-gw-v2.zk.link", + Network::DevNet => "https://dev-gw-v1.zk.link", } } } @@ -28,6 +31,7 @@ impl FromStr for Network { match s { "mainet" => Ok(Network::MainNet), "testnet" => Ok(Network::TestNet), + "devnet" => Ok(Network::DevNet), _ => Err(RpcError::InvalidNetwork), } } diff --git a/provider/tests/test_rpc.rs b/provider/tests/test_rpc.rs new file mode 100644 index 00000000..3c53348c --- /dev/null +++ b/provider/tests/test_rpc.rs @@ -0,0 +1,77 @@ +#[cfg(test)] +mod test { + use jsonrpsee::http_client::HttpClientBuilder; + use std::str::FromStr; + use zklink_sdk_provider::ZkLinkRpcClient; + use zklink_sdk_signers::eth_signer::EthSigner; + use zklink_sdk_signers::zklink_signer::{PubKeyHash, ZkLinkSigner}; + use zklink_sdk_types::basic_types::BigUint; + use zklink_sdk_types::basic_types::{ + AccountId, ChainId, Nonce, SubAccountId, TimeStamp, TokenId, ZkLinkAddress, + }; + use zklink_sdk_types::tx_builder::ChangePubKeyBuilder; + use zklink_sdk_types::tx_type::change_pubkey::ChangePubKey; + use zklink_sdk_types::tx_type::zklink_tx::ZkLinkTx; + use zklink_sdk_types::tx_type::ZkSignatureTrait; + + #[tokio::test] + async fn test_send_change_pubkey() { + let private_key = "be725250b123a39dab5b7579334d5888987c72a58f4508062545fe6e08ca94f4"; + let eth_signer = EthSigner::try_from(private_key).unwrap(); + let zklink_signer = ZkLinkSigner::new_from_hex_eth_signer(private_key).unwrap(); + let main_contract = "0x5505a8cD4594Dbf79d8C59C0Df1414AB871CA896"; + let l1_client_id = 80001; + let new_pubkey_hash = "0xd8d5fb6a6caef06aa3dc2abdcdc240987e5330fe"; + let ts = 1696595303; + //auth type 'ECDSA' + let builder = ChangePubKeyBuilder { + chain_id: ChainId(1), + account_id: AccountId(10), + sub_account_id: SubAccountId(1), + new_pubkey_hash: PubKeyHash::from_hex(new_pubkey_hash).unwrap(), + fee_token: TokenId(18), + fee: BigUint::from(100000000000000u64), + nonce: Nonce(1), + eth_signature: None, + timestamp: TimeStamp(ts), + }; + let change_pubkey = ChangePubKey::new(builder); + let message = change_pubkey + .to_eip712_request_payload( + l1_client_id, + &ZkLinkAddress::from_str(main_contract).unwrap(), + ) + .unwrap(); + let signature = eth_signer + .sign_message(message.raw_data.as_bytes()) + .unwrap(); + let builder_with_sig = ChangePubKeyBuilder { + chain_id: ChainId(1), + account_id: AccountId(10), + sub_account_id: SubAccountId(1), + new_pubkey_hash: PubKeyHash::from_hex(new_pubkey_hash).unwrap(), + fee_token: TokenId(18), + fee: BigUint::from(100000000000000u64), + nonce: Nonce(1), + eth_signature: Some(signature), + timestamp: TimeStamp(ts), + }; + let mut tx = ChangePubKey::new(builder_with_sig); + tx.sign(&zklink_signer).unwrap(); + let submitter_signature = tx.submitter_signature(&zklink_signer).unwrap(); + + //use jsonrpsee + let client = HttpClientBuilder::default() + .build("https://dev-gw-v1.zk.link") + .unwrap(); + let ret = client + .tx_submit( + ZkLinkTx::ChangePubKey(Box::new(tx.clone())), + None, + Some(submitter_signature), + ) + .await + .unwrap(); + println!("{:?}", ret) + } +} diff --git a/signers/src/eth_signer/error.rs b/signers/src/eth_signer/error.rs index cdabfa72..323a4b1a 100644 --- a/signers/src/eth_signer/error.rs +++ b/signers/src/eth_signer/error.rs @@ -1,4 +1,6 @@ use thiserror::Error; +#[cfg(target_arch = "wasm32")] +use wasm_bindgen::JsValue; #[derive(Debug, Error, PartialEq)] pub enum EthSignerError { @@ -31,3 +33,10 @@ pub enum EthSignerError { #[error("{0}")] CustomError(String), } + +#[cfg(target_arch = "wasm32")] +impl From for JsValue { + fn from(error: EthSignerError) -> Self { + JsValue::from_str(&format!("error: {error}")) + } +} diff --git a/signers/src/eth_signer/mod.rs b/signers/src/eth_signer/mod.rs index 2acee1a5..64c0cc4b 100644 --- a/signers/src/eth_signer/mod.rs +++ b/signers/src/eth_signer/mod.rs @@ -3,8 +3,6 @@ pub mod eip712; pub mod error; pub mod packed_eth_signature; pub mod pk_signer; -#[cfg(target_arch = "wasm32")] -pub mod wasm_binding; pub use primitive_types::{H160, H256, U256}; pub type Address = H160; diff --git a/signers/src/eth_signer/wasm_binding.rs b/signers/src/eth_signer/wasm_binding.rs deleted file mode 100644 index 02def8df..00000000 --- a/signers/src/eth_signer/wasm_binding.rs +++ /dev/null @@ -1,8 +0,0 @@ -use crate::eth_signer::error::EthSignerError; -use wasm_bindgen::JsValue; - -impl From for JsValue { - fn from(error: EthSignerError) -> Self { - JsValue::from_str(&format!("error: {error}")) - } -} diff --git a/signers/src/zklink_signer/error.rs b/signers/src/zklink_signer/error.rs index 9b905b2b..9361b6e0 100644 --- a/signers/src/zklink_signer/error.rs +++ b/signers/src/zklink_signer/error.rs @@ -1,5 +1,7 @@ use crate::eth_signer::error::EthSignerError; use thiserror::Error; +#[cfg(target_arch = "wasm32")] +use wasm_bindgen::JsValue; #[derive(Debug, Error)] pub enum ZkSignerError { @@ -30,3 +32,10 @@ impl ZkSignerError { Self::InvalidPrivKey(s.to_string()) } } + +#[cfg(target_arch = "wasm32")] +impl From for JsValue { + fn from(error: ZkSignerError) -> Self { + JsValue::from_str(&format!("error: {error}")) + } +} diff --git a/signers/src/zklink_signer/mod.rs b/signers/src/zklink_signer/mod.rs index 8234ea33..68af57f4 100644 --- a/signers/src/zklink_signer/mod.rs +++ b/signers/src/zklink_signer/mod.rs @@ -4,8 +4,6 @@ pub mod pubkey_hash; pub mod public_key; pub mod signature; pub mod utils; -#[cfg(target_arch = "wasm32")] -pub mod wasm_binding; pub use franklin_crypto::bellman::pairing::bn256::{Bn256 as Engine, Fr}; use franklin_crypto::rescue::bn256::Bn256RescueParams; diff --git a/signers/src/zklink_signer/wasm_binding.rs b/signers/src/zklink_signer/wasm_binding.rs deleted file mode 100644 index c7e08f2c..00000000 --- a/signers/src/zklink_signer/wasm_binding.rs +++ /dev/null @@ -1,8 +0,0 @@ -use crate::zklink_signer::error::ZkSignerError; -use wasm_bindgen::JsValue; - -impl From for JsValue { - fn from(error: ZkSignerError) -> Self { - JsValue::from_str(&format!("error: {error}")) - } -} From 8682f5bf96d5dd2371869a6eafda569732b5f3c6 Mon Sep 17 00:00:00 2001 From: nickwest Date: Thu, 12 Oct 2023 00:25:02 -0700 Subject: [PATCH 6/6] fix review suggests and add all txs example --- Makefile | 3 + bindings/wasm/Cargo.toml | 1 + bindings/wasm/src/lib.rs | 2 - bindings/wasm/src/rpc_client.rs | 18 ++- bindings/wasm/src/signer.rs | 40 ++++++ bindings/wasm/src/tx_types/forced_exit.rs | 66 ++++++++++ bindings/wasm/src/tx_types/mod.rs | 3 + bindings/wasm/src/tx_types/order_matching.rs | 108 +++++++++++++++++ bindings/wasm/src/tx_types/withdraw.rs | 70 +++++++++++ bindings/wasm/src/wallet.rs | 114 ------------------ bindings/wasm/tests/test_rpc.rs | 51 ++++---- .../Javascript/js-example/1_change_pubkey.js | 4 +- examples/Javascript/js-example/2_transfer.js | 2 +- .../Javascript/js-example/3_order_matching.js | 33 +++++ examples/Javascript/js-example/4_withdraw.js | 28 +++++ .../Javascript/js-example/5_forced_exit.js | 27 +++++ .../js-example/{rpc.js => 6_rpc.js} | 2 +- examples/Javascript/js-example/index.html | 5 +- interface/src/sign_order.rs | 8 +- interface/src/signer.rs | 9 +- provider/src/error.rs | 6 +- provider/src/lib.rs | 15 +-- provider/tests/test_rpc.rs | 69 ++++++++++- 23 files changed, 514 insertions(+), 170 deletions(-) create mode 100644 bindings/wasm/src/tx_types/forced_exit.rs create mode 100644 bindings/wasm/src/tx_types/order_matching.rs create mode 100644 bindings/wasm/src/tx_types/withdraw.rs delete mode 100644 bindings/wasm/src/wallet.rs create mode 100644 examples/Javascript/js-example/3_order_matching.js create mode 100644 examples/Javascript/js-example/4_withdraw.js create mode 100644 examples/Javascript/js-example/5_forced_exit.js rename examples/Javascript/js-example/{rpc.js => 6_rpc.js} (98%) diff --git a/Makefile b/Makefile index 6335974a..73eeb893 100644 --- a/Makefile +++ b/Makefile @@ -78,6 +78,9 @@ wasm: wasm-pack build --release --target=web --out-name=zklink-sdk-web --out-dir=web-dist && \ wasm-pack build --release --target=nodejs --out-name=zklink-sdk-node --out-dir=node-dist #wasm-pack build --release --target=bundler --out-name=zklink-bundler-node --out-dir=dist +test_wasm: + cd ${ROOT_DIR}/bindings/wasm && \ + wasm-pack test --firefox --headless -- --test test_rpc run_example_go_%: ${ROOT_DIR}/examples/Golang/%.go diff --git a/bindings/wasm/Cargo.toml b/bindings/wasm/Cargo.toml index 80e781a1..86277e24 100644 --- a/bindings/wasm/Cargo.toml +++ b/bindings/wasm/Cargo.toml @@ -18,6 +18,7 @@ serde-wasm-bindgen = "0.5" getrandom = { version = "0.2.10", features = ["js"] } web-sys = "0.3" hex = "0.4.3" +serde = "1.0.137" reqwest = { version = "0.11", default-features = false, features = ["blocking", "json", "rustls-tls"] } [target.'cfg(target_arch = "wasm32")'.dependencies] diff --git a/bindings/wasm/src/lib.rs b/bindings/wasm/src/lib.rs index 5bb43983..5894b515 100644 --- a/bindings/wasm/src/lib.rs +++ b/bindings/wasm/src/lib.rs @@ -3,7 +3,5 @@ pub mod rpc_client; pub mod rpc_type_converter; pub mod signer; pub mod tx_types; -// pub mod wallet; -// pub mod error; extern crate getrandom; diff --git a/bindings/wasm/src/rpc_client.rs b/bindings/wasm/src/rpc_client.rs index 0f27cd3d..83304a1a 100644 --- a/bindings/wasm/src/rpc_client.rs +++ b/bindings/wasm/src/rpc_client.rs @@ -4,6 +4,7 @@ use jsonrpsee::core::params::ArrayParams; use jsonrpsee::core::traits::ToRpcParams; use jsonrpsee::types::request::Request; use jsonrpsee::types::Id; +use serde::Deserialize; use std::collections::HashMap; use std::str::FromStr; use wasm_bindgen::prelude::wasm_bindgen; @@ -24,6 +25,13 @@ use zklink_sdk_types::signatures::TxLayer1Signature as TypesTxLayer1Signature; use zklink_sdk_types::tx_type::zklink_tx::ZkLinkTx; use zklink_sdk_types::tx_type::zklink_tx::ZkLinkTxType; +#[derive(Deserialize, Clone)] +pub struct ErrMsg { + #[allow(dead_code)] + code: i32, + message: String, +} + macro_rules! rpc_request { ($method:expr,$builder:expr, $server_url:expr, $resp_type: ty) => {{ let params = $builder @@ -47,10 +55,16 @@ macro_rules! rpc_request { let resp = serde_json::from_value::<$resp_type>(result.clone()); match resp { Ok(resp) => Ok(serde_wasm_bindgen::to_value(&resp)?), - Err(_e) => Err(RpcError::ParseJsonError.into()), + Err(e) => Err(RpcError::ParseJsonError(e.to_string()).into()), + } + } else if let Some(&ref error) = res.get("error") { + let err_msg = serde_json::from_value::(error.clone()); + match err_msg { + Ok(msg) => Err(RpcError::GetErrorResult(msg.message).into()), + Err(_) => Err(RpcError::GetErrorResult("Other error response".to_string()).into()), } } else { - Err(RpcError::ParseJsonError.into()) + Err(RpcError::GetErrorResult("Other error response".to_string()).into()) } }}; } diff --git a/bindings/wasm/src/signer.rs b/bindings/wasm/src/signer.rs index 9172d387..8e442339 100644 --- a/bindings/wasm/src/signer.rs +++ b/bindings/wasm/src/signer.rs @@ -1,12 +1,20 @@ use crate::tx_types::change_pubkey::{ChangePubKey, Create2Data}; +use crate::tx_types::forced_exit::ForcedExit; +use crate::tx_types::order_matching::{Order, OrderMatching}; use crate::tx_types::transfer::Transfer; +use crate::tx_types::withdraw::Withdraw; use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::JsValue; use zklink_sdk_interface::signer::Signer as InterfaceSigner; use zklink_sdk_types::basic_types::ZkLinkAddress; use zklink_sdk_types::tx_type::change_pubkey::ChangePubKey as TxChangePubKey; use zklink_sdk_types::tx_type::change_pubkey::Create2Data as ChangePubKeyCreate2Data; +use zklink_sdk_types::tx_type::forced_exit::ForcedExit as TxForcedExit; +use zklink_sdk_types::tx_type::order_matching::{ + Order as TxOrder, OrderMatching as TxOrderMatching, +}; use zklink_sdk_types::tx_type::transfer::Transfer as TxTransfer; +use zklink_sdk_types::tx_type::withdraw::Withdraw as TxWithdraw; use zklink_sdk_types::tx_type::zklink_tx::ZkLinkTx; #[wasm_bindgen] @@ -67,6 +75,38 @@ impl Signer { Ok(serde_wasm_bindgen::to_value(&signature)?) } + #[wasm_bindgen(js_name=createSignedOrder)] + pub fn create_signed_order(&self, order: Order) -> Result { + let inner_order = order.get_inner_order()?; + let mut order: TxOrder = serde_wasm_bindgen::from_value(inner_order)?; + let signed_order = self.inner.sign_order(&mut order)?; + Ok(serde_wasm_bindgen::to_value(&signed_order)?) + } + + #[wasm_bindgen(js_name=signOrderMatching)] + pub fn sign_order_matching(&self, tx: OrderMatching) -> Result { + let inner_tx = tx.get_inner_tx()?; + let order_matching: TxOrderMatching = serde_wasm_bindgen::from_value(inner_tx)?; + let signature = self.inner.sign_order_matching(order_matching)?; + Ok(serde_wasm_bindgen::to_value(&signature)?) + } + + #[wasm_bindgen(js_name=signWithdraw)] + pub fn sign_withdraw(&self, tx: Withdraw, token_symbol: &str) -> Result { + let inner_tx = tx.get_inner_tx()?; + let withdraw: TxWithdraw = serde_wasm_bindgen::from_value(inner_tx)?; + let signature = self.inner.sign_withdraw(withdraw, token_symbol)?; + Ok(serde_wasm_bindgen::to_value(&signature)?) + } + + #[wasm_bindgen(js_name=signForcedExit)] + pub fn sign_forced_exit(&self, tx: ForcedExit) -> Result { + let inner_tx = tx.get_inner_tx()?; + let forced_exit: TxForcedExit = serde_wasm_bindgen::from_value(inner_tx)?; + let signature = self.inner.sign_forced_exit(forced_exit)?; + Ok(serde_wasm_bindgen::to_value(&signature)?) + } + #[wasm_bindgen(js_name=submitterSignature)] pub fn submitter_signature(&self, tx: JsValue) -> Result { let zklink_tx: ZkLinkTx = serde_wasm_bindgen::from_value(tx)?; diff --git a/bindings/wasm/src/tx_types/forced_exit.rs b/bindings/wasm/src/tx_types/forced_exit.rs new file mode 100644 index 00000000..901f997a --- /dev/null +++ b/bindings/wasm/src/tx_types/forced_exit.rs @@ -0,0 +1,66 @@ +use std::str::FromStr; +use wasm_bindgen::prelude::wasm_bindgen; +use wasm_bindgen::JsValue; +use zklink_sdk_types::basic_types::{BigUint, ZkLinkAddress}; +use zklink_sdk_types::tx_builder::ForcedExitBuilder as TxForcedExitBuilder; +use zklink_sdk_types::tx_type::forced_exit::ForcedExit as ForcedExitTx; + +#[wasm_bindgen] +pub struct ForcedExit { + inner: ForcedExitTx, +} + +#[wasm_bindgen] +impl ForcedExit { + pub fn get_inner_tx(&self) -> Result { + Ok(serde_wasm_bindgen::to_value(&self.inner)?) + } +} + +#[wasm_bindgen] +pub struct ForcedExitBuilder { + inner: TxForcedExitBuilder, +} + +#[wasm_bindgen] +impl ForcedExitBuilder { + #[wasm_bindgen(constructor)] + pub fn new( + to_chain_id: u8, + initiator_account_id: u32, + initiator_sub_account_id: u8, + target_sub_account_id: u8, + target: String, + l2_source_token: u32, + l1_target_token: u32, + exit_amount: String, + initiator_nonce: u32, + ts: u32, + ) -> Result { + let inner = TxForcedExitBuilder { + to_chain_id: to_chain_id.into(), + initiator_account_id: initiator_account_id.into(), + initiator_sub_account_id: initiator_sub_account_id.into(), + target: ZkLinkAddress::from_hex(&target)?, + l2_source_token: l2_source_token.into(), + timestamp: ts.into(), + l1_target_token: l1_target_token.into(), + initiator_nonce: initiator_nonce.into(), + target_sub_account_id: target_sub_account_id.into(), + exit_amount: BigUint::from_str(&exit_amount).unwrap(), + }; + Ok(ForcedExitBuilder { inner }) + } + + #[wasm_bindgen] + pub fn build(self) -> ForcedExit { + ForcedExit { + inner: ForcedExitTx::new(self.inner), + } + } +} + +#[wasm_bindgen(js_name=newForcedExit)] +pub fn new_forced_exit(builder: ForcedExitBuilder) -> ForcedExit { + builder.build() +} diff --git a/bindings/wasm/src/tx_types/mod.rs b/bindings/wasm/src/tx_types/mod.rs index 21c79913..bcb8e962 100644 --- a/bindings/wasm/src/tx_types/mod.rs +++ b/bindings/wasm/src/tx_types/mod.rs @@ -1,2 +1,5 @@ pub mod change_pubkey; +pub mod forced_exit; +pub mod order_matching; pub mod transfer; +pub mod withdraw; diff --git a/bindings/wasm/src/tx_types/order_matching.rs b/bindings/wasm/src/tx_types/order_matching.rs new file mode 100644 index 00000000..c27ae35e --- /dev/null +++ b/bindings/wasm/src/tx_types/order_matching.rs @@ -0,0 +1,108 @@ +use std::str::FromStr; +use wasm_bindgen::prelude::wasm_bindgen; +use wasm_bindgen::JsValue; +use zklink_sdk_types::basic_types::BigUint; +use zklink_sdk_types::tx_builder::OrderMatchingBuilder as TxOrderMatchingBuilder; +use zklink_sdk_types::tx_type::order_matching::{ + Order as OrderTx, OrderMatching as OrderMatchingTx, +}; +#[wasm_bindgen] +pub struct Order { + inner: OrderTx, +} + +#[wasm_bindgen] +pub struct OrderMatching { + inner: OrderMatchingTx, +} +#[wasm_bindgen] +impl Order { + #[wasm_bindgen(constructor)] + pub fn new( + account_id: u32, + sub_account_id: u8, + slot_id: u32, + nonce: u32, + base_token_id: u32, + quote_token_id: u32, + amount: String, + price: String, + is_sell: bool, + fee_ratio1: u8, + fee_ratio2: u8, + ) -> Order { + Order { + inner: OrderTx { + account_id: account_id.into(), + sub_account_id: sub_account_id.into(), + slot_id: slot_id.into(), + nonce: nonce.into(), + base_token_id: base_token_id.into(), + quote_token_id: quote_token_id.into(), + amount: BigUint::from_str(&amount).unwrap(), + price: BigUint::from_str(&price).unwrap(), + is_sell: is_sell as u8, + fee_ratio1, + fee_ratio2, + signature: Default::default(), + }, + } + } + + pub fn get_inner_order(&self) -> Result { + Ok(serde_wasm_bindgen::to_value(&self.inner)?) + } +} + +#[wasm_bindgen] +impl OrderMatching { + pub fn get_inner_tx(&self) -> Result { + Ok(serde_wasm_bindgen::to_value(&self.inner)?) + } +} + +#[wasm_bindgen] +pub struct OrderMatchingBuilder { + inner: TxOrderMatchingBuilder, +} + +#[wasm_bindgen] +impl OrderMatchingBuilder { + #[wasm_bindgen(constructor)] + pub fn new( + account_id: u32, + sub_account_id: u8, + taker: JsValue, + maker: JsValue, + fee: String, + fee_token: u32, + expect_base_amount: String, + expect_quote_amount: String, + ) -> Result { + let maker: OrderTx = serde_wasm_bindgen::from_value(maker)?; + let taker: OrderTx = serde_wasm_bindgen::from_value(taker)?; + let inner = TxOrderMatchingBuilder { + account_id: account_id.into(), + sub_account_id: sub_account_id.into(), + taker, + fee: BigUint::from_str(&fee).unwrap(), + fee_token: fee_token.into(), + expect_base_amount: BigUint::from_str(&expect_base_amount).unwrap(), + maker, + expect_quote_amount: BigUint::from_str(&expect_quote_amount).unwrap(), + }; + Ok(OrderMatchingBuilder { inner }) + } + + #[wasm_bindgen] + pub fn build(self) -> OrderMatching { + OrderMatching { + inner: OrderMatchingTx::new(self.inner), + } + } +} + +#[wasm_bindgen(js_name=newOrderMatching)] +pub fn new_order_matching(builder: OrderMatchingBuilder) -> OrderMatching { + builder.build() +} diff --git a/bindings/wasm/src/tx_types/withdraw.rs b/bindings/wasm/src/tx_types/withdraw.rs new file mode 100644 index 00000000..5e19ade6 --- /dev/null +++ b/bindings/wasm/src/tx_types/withdraw.rs @@ -0,0 +1,70 @@ +use std::str::FromStr; +use wasm_bindgen::prelude::wasm_bindgen; +use wasm_bindgen::JsValue; +use zklink_sdk_types::basic_types::{BigUint, ZkLinkAddress}; +use zklink_sdk_types::tx_builder::WithdrawBuilder as TxWithdrawBuilder; +use zklink_sdk_types::tx_type::withdraw::Withdraw as WithdrawTx; + +#[wasm_bindgen] +pub struct Withdraw { + inner: WithdrawTx, +} + +#[wasm_bindgen] +impl Withdraw { + pub fn get_inner_tx(&self) -> Result { + Ok(serde_wasm_bindgen::to_value(&self.inner)?) + } +} + +#[wasm_bindgen] +pub struct WithdrawBuilder { + inner: TxWithdrawBuilder, +} + +#[wasm_bindgen] +impl WithdrawBuilder { + #[wasm_bindgen(constructor)] + pub fn new( + account_id: u32, + sub_account_id: u8, + to_chain_id: u8, + to_address: String, + l2_source_token: u32, + fee: String, + fast_withdraw: bool, + withdraw_fee_ratio: u16, + l1_target_token: u32, + amount: String, + nonce: u32, + ts: u32, + ) -> Result { + let inner = TxWithdrawBuilder { + account_id: account_id.into(), + sub_account_id: sub_account_id.into(), + to_chain_id: to_chain_id.into(), + to_address: ZkLinkAddress::from_hex(&to_address)?, + l2_source_token: l2_source_token.into(), + fee: BigUint::from_str(&fee).unwrap(), + nonce: nonce.into(), + fast_withdraw, + withdraw_fee_ratio, + timestamp: ts.into(), + amount: BigUint::from_str(&amount).unwrap(), + l1_target_token: l1_target_token.into(), + }; + Ok(WithdrawBuilder { inner }) + } + + #[wasm_bindgen] + pub fn build(self) -> Withdraw { + Withdraw { + inner: WithdrawTx::new(self.inner), + } + } +} + +#[wasm_bindgen(js_name=newWithdraw)] +pub fn new_withdraw(builder: WithdrawBuilder) -> Withdraw { + builder.build() +} diff --git a/bindings/wasm/src/wallet.rs b/bindings/wasm/src/wallet.rs deleted file mode 100644 index 9b0a527f..00000000 --- a/bindings/wasm/src/wallet.rs +++ /dev/null @@ -1,114 +0,0 @@ -use crate::error::ClientError; -use crate::signer::Signer; -use crate::{AccountType, Wallet}; -use num::BigUint; -use zklink_sdk_interface::{ChangePubKeyAuthRequest, TxSignature}; -use zklink_sdk_provider::response::{AccountQuery, ChainResp, TokenResp, AccountInfoResp}; -use zklink_sdk_provider::rpc::ZkLinkRpcClient; -use zklink_sdk_signers::eth_signer::eth_signature::TxEthSignature; -use zklink_sdk_signers::zklink_signer::pk_signer::ZkLinkSigner; - -use zklink_sdk_signers::zklink_signer::pubkey_hash::PubKeyHash; -use zklink_sdk_signers::zklink_signer::signature::ZkLinkSignature; -use zklink_sdk_types::basic_types::params::MAIN_SUB_ACCOUNT_ID; -use zklink_sdk_types::basic_types::tx_hash::TxHash; -use zklink_sdk_types::basic_types::{AccountId, ChainId, Nonce, SubAccountId, TokenId, ZkLinkAddress}; -use zklink_sdk_types::tx_type::order_matching::Order; -use std::collections::HashMap; -use wasm_bindgen::JsValue; - -#[wasm_bindgen] -pub struct Wallet { - pub provider: ZkLinkRpcProvider, - pub address: ZklinkAddress, - pub account_info: AccountInfoResp, - pub account_type: AccountType, - pub chains: HashMap, - pub tokens: HashMap, -} - -#[wasm_bindgen] -impl Wallet { - #[wasm_bindgen] - pub async fn new( - provider: ZkLinkRpcProvider, - address: ZkLinkAddress, - account_type: AccountType, - ) -> Result { - let chains = provider.get_support_chains().await?; - let chains = chains.iter().map(|c| (c.chain_id, c.clone())).collect(); - let tokens = provider.tokens().await?; - let account_info = provider - .account_info(AccountQuery::Address(address.clone())) - .await?; - - let wallet = Wallet { - provider, - address, - account_info, - account_type, - chains, - tokens, - }; - - Ok(wallet) - } - - /// Returns the wallet address. - #[wasm_bindgen] - pub fn address(&self) -> ZkLinkAddress { - self.address.clone() - } - - /// Returns the current account id - #[wasm_bindgen] - pub fn account_id(&self) -> AccountId { - self.account_info.id - } - - /// Returns the current account pub key hash - #[wasm_bindgen] - pub fn account_pubkey_hash(&self) -> PubKeyHash { - self.account_info.pub_key_hash - } - - #[wasm_bindgen] - pub fn account_nonce(&self) -> Nonce { - self.account_info.nonce - } - - /// Updates account info stored in the wallet. - #[wasm_bindgen] - pub async fn update_account_info(&mut self) -> Result<(), JsValue> { - self.account_info = self - .provider - .account_info(AccountQuery::Address(self.address.clone())) - .await?; - - Ok(()) - } - - /// Returns `true` if signing key for account was set in zkLink network. - /// In other words, returns `true` if `ChangePubKey` operation was performed for the - /// account. - /// - /// If this method has returned `false`, one must send a `ChangePubKey` transaction - /// via `Wallet::start_change_pubkey` method. - #[wasm_bindgen] - pub async fn is_signing_key_set(&self) -> Result { - let signer_pub_key_hash = self.signer.pub_key_hash; - - let key_set = self.account_pubkey_hash() == signer_pub_key_hash; - Ok(key_set) - } - - #[wasm_bindgen] - pub fn get_chain(&self, chain_id: &ChainId) -> Option { - self.chains.get(chain_id).cloned() - } - - #[wasm_bindgen] - pub fn get_token(&self, token_id: &TokenId) -> Option { - self.tokens.get(token_id).cloned() - } -} diff --git a/bindings/wasm/tests/test_rpc.rs b/bindings/wasm/tests/test_rpc.rs index 5d441756..607dc807 100644 --- a/bindings/wasm/tests/test_rpc.rs +++ b/bindings/wasm/tests/test_rpc.rs @@ -12,36 +12,35 @@ use zklink_sdk_types::tx_type::change_pubkey::ChangePubKey; use zklink_sdk_types::tx_type::zklink_tx::ZkLinkTx as TypesZkLinkTx; use zklink_sdk_types::tx_type::ZkSignatureTrait; use zklink_sdk_wasm::rpc_client::RpcClient; -use zklink_sdk_wasm::rpc_type_converter::{AccountQuery, AccountQueryType, ZkLinkTx}; +use zklink_sdk_wasm::rpc_type_converter::{AccountQuery, AccountQueryType}; wasm_bindgen_test_configure!(run_in_worker); -// #[wasm_bindgen_test] -// async fn test_get_tokens() { -// let client = RpcClient::new("testnet"); -// let ret = client.tokens().await; -// if let Err(e) = ret { -// web_sys::console::log_1(&JsValue::from_str(&format!("{:?}", e))); -// } else { -// web_sys::console::log_1(&JsValue::from_str(&format!("{:?}", ret.unwrap()))); -// } -// // assert!(ret.is_err()); -// } -// -// #[wasm_bindgen_test] -// async fn test_account_query() { -// let client = RpcClient::new("testnet"); -// let account_id = AccountQuery::new(AccountQueryType::AccountId, "5".to_string()); -// let account_resp = client.account_query(account_id.into(), None, None).await; -// if let Err(e) = account_resp { -// web_sys::console::log_1(&JsValue::from_str(&format!("{:?}", e))); -// } else { -// web_sys::console::log_1(&JsValue::from_str(&format!("{:?}", account_resp.unwrap()))); -// } -// } +#[wasm_bindgen_test] +async fn test_get_tokens() { + let client = RpcClient::new("testnet"); + let ret = client.tokens().await; + if let Err(e) = ret { + web_sys::console::log_1(&JsValue::from_str(&format!("{:?}", e))); + } else { + web_sys::console::log_1(&JsValue::from_str(&format!("{:?}", ret.unwrap()))); + } + // assert!(ret.is_err()); +} + +#[wasm_bindgen_test] +async fn test_account_query() { + let client = RpcClient::new("testnet"); + let account_id = AccountQuery::new(AccountQueryType::AccountId, "5".to_string()); + let account_resp = client.account_query(account_id.into(), None, None).await; + if let Err(e) = account_resp { + web_sys::console::log_1(&JsValue::from_str(&format!("{:?}", e))); + } else { + web_sys::console::log_1(&JsValue::from_str(&format!("{:?}", account_resp.unwrap()))); + } +} #[wasm_bindgen_test] async fn test_send_change_pubkey() { - web_sys::console::log_1(&JsValue::from_str("123")); let client = RpcClient::new("devnet"); let private_key = "be725250b123a39dab5b7579334d5888987c72a58f4508062545fe6e08ca94f4"; let eth_signer = EthSigner::try_from(private_key).unwrap(); @@ -62,7 +61,7 @@ async fn test_send_change_pubkey() { eth_signature: None, timestamp: TimeStamp(ts), }; - let mut change_pubkey = ChangePubKey::new(builder); + let change_pubkey = ChangePubKey::new(builder); let message = change_pubkey .to_eip712_request_payload( l1_client_id, diff --git a/examples/Javascript/js-example/1_change_pubkey.js b/examples/Javascript/js-example/1_change_pubkey.js index c53a27e3..8eb9925a 100644 --- a/examples/Javascript/js-example/1_change_pubkey.js +++ b/examples/Javascript/js-example/1_change_pubkey.js @@ -20,7 +20,7 @@ async function testEcdsaAuth() { let submitter_signature = signer.submitterSignature(tx_signature.tx); console.log(submitter_signature); //send to zklink - let rpc_client = new wasm.RpcClient("devnet"); + let rpc_client = new wasm.RpcClient("testnet"); let tx_hash = await rpc_client.sendTransaction(tx_signature.tx,null,submitter_signature); console.log(tx_hash); @@ -65,4 +65,4 @@ async function testCreate2() { } await testEcdsaAuth(); -await testCreate2(); +// await testCreate2(); diff --git a/examples/Javascript/js-example/2_transfer.js b/examples/Javascript/js-example/2_transfer.js index 68f0c420..e60a9e1a 100644 --- a/examples/Javascript/js-example/2_transfer.js +++ b/examples/Javascript/js-example/2_transfer.js @@ -15,7 +15,7 @@ async function main() { let submitter_signature = signer.submitterSignature(signature.tx); console.log(submitter_signature); let rpc_client = new wasm.RpcClient("testnet"); - let l1_signature = new wasm.TxL1Signature(wasm.L1SignatureType.Eth,signature.eth_signature); + let l1_signature = new wasm.TxLayer1Signature(wasm.L1SignatureType.Eth,signature.eth_signature); let tx_hash = await rpc_client.sendTransaction(signature.tx,l1_signature,submitter_signature); console.log(tx_hash); diff --git a/examples/Javascript/js-example/3_order_matching.js b/examples/Javascript/js-example/3_order_matching.js new file mode 100644 index 00000000..c95712f5 --- /dev/null +++ b/examples/Javascript/js-example/3_order_matching.js @@ -0,0 +1,33 @@ +import init, * as wasm from "./web-dist/zklink-sdk-web.js"; + +async function main() { + await init(); + const private_key = "be725250b123a39dab5b7579334d5888987c72a58f4508062545fe6e08ca94f4"; + try { + + let signer = new wasm.Signer(private_key); + //maker = taker = submitter + let maker_order = new wasm.Order(5,1,1,1,18,17,"10000000000000","10000000000",true,5,3); + let maker = signer.createSignedOrder(maker_order); + console.log(maker); + let taker_order = new wasm.Order(5,1,1,1,18,17,"10000000000000","10000000000",false,5,3); + let taker = signer.createSignedOrder(taker_order); + console.log(taker); + let tx_builder = new wasm.OrderMatchingBuilder(10, 1, taker, maker, "1000000000", 18,"10000000000000000", "10000000000000000"); + let order_matching = wasm.newOrderMatching(tx_builder); + let signature = signer.signOrderMatching(order_matching); + console.log(signature); + + let submitter_signature = signer.submitterSignature(signature.tx); + console.log(submitter_signature); + let rpc_client = new wasm.RpcClient("testnet"); + let tx_hash = await rpc_client.sendTransaction(signature.tx,null,submitter_signature); + console.log(tx_hash); + + } catch (error) { + console.error(error); + } + +} + +main(); diff --git a/examples/Javascript/js-example/4_withdraw.js b/examples/Javascript/js-example/4_withdraw.js new file mode 100644 index 00000000..7028ea01 --- /dev/null +++ b/examples/Javascript/js-example/4_withdraw.js @@ -0,0 +1,28 @@ +import init, * as wasm from "./web-dist/zklink-sdk-web.js"; + +async function main() { + await init(); + const private_key = "be725250b123a39dab5b7579334d5888987c72a58f4508062545fe6e08ca94f4"; + const to_address = "0x5505a8cD4594Dbf79d8C59C0Df1414AB871CA896"; + const ts = Math.floor(Date.now() / 1000); + try { + let tx_builder = new wasm.WithdrawBuilder(10, 1, 1, to_address,18, "100000000000000", false,10,18,"10000000000000000", 1,ts); + let withdraw = wasm.newWithdraw(tx_builder); + let signer = new wasm.Signer(private_key); + let signature = signer.signWithdraw(withdraw,"USDC") + console.log(signature); + + let submitter_signature = signer.submitterSignature(signature.tx); + console.log(submitter_signature); + let rpc_client = new wasm.RpcClient("testnet"); + let l1_signature = new wasm.TxLayer1Signature(wasm.L1SignatureType.Eth,signature.eth_signature); + let tx_hash = await rpc_client.sendTransaction(signature.tx,l1_signature,submitter_signature); + console.log(tx_hash); + + } catch (error) { + console.error(error); + } + +} + +main(); diff --git a/examples/Javascript/js-example/5_forced_exit.js b/examples/Javascript/js-example/5_forced_exit.js new file mode 100644 index 00000000..185261c8 --- /dev/null +++ b/examples/Javascript/js-example/5_forced_exit.js @@ -0,0 +1,27 @@ +import init, * as wasm from "./web-dist/zklink-sdk-web.js"; + +async function main() { + await init(); + const private_key = "be725250b123a39dab5b7579334d5888987c72a58f4508062545fe6e08ca94f4"; + const to_address = "0x5505a8cD4594Dbf79d8C59C0Df1414AB871CA896"; + const ts = Math.floor(Date.now() / 1000); + try { + let tx_builder = new wasm.ForcedExitBuilder(1,10, 1, 1, to_address,18, 18,"100000000000000", 1,ts); + let forced_exit = wasm.newForcedExit(tx_builder); + let signer = new wasm.Signer(private_key); + let signature = signer.signForcedExit(forced_exit) + console.log(signature); + + let submitter_signature = signer.submitterSignature(signature.tx); + console.log(submitter_signature); + let rpc_client = new wasm.RpcClient("testnet"); + let tx_hash = await rpc_client.sendTransaction(signature.tx,null,submitter_signature); + console.log(tx_hash); + + } catch (error) { + console.error(error); + } + +} + +main(); diff --git a/examples/Javascript/js-example/rpc.js b/examples/Javascript/js-example/6_rpc.js similarity index 98% rename from examples/Javascript/js-example/rpc.js rename to examples/Javascript/js-example/6_rpc.js index bad186aa..764396db 100644 --- a/examples/Javascript/js-example/rpc.js +++ b/examples/Javascript/js-example/6_rpc.js @@ -2,7 +2,7 @@ import init, * as wasm from "./web-dist/zklink-sdk-web.js"; async function main() { await init(); try { - let client = new wasm.RpcClient("mainet"); + let client = new wasm.RpcClient("testnet"); // 1.getSupportTokens let tokens = await client.getSupportTokens(); console.log(tokens); diff --git a/examples/Javascript/js-example/index.html b/examples/Javascript/js-example/index.html index 2de4a22b..0c7edc07 100644 --- a/examples/Javascript/js-example/index.html +++ b/examples/Javascript/js-example/index.html @@ -6,9 +6,10 @@ - \ No newline at end of file diff --git a/interface/src/sign_order.rs b/interface/src/sign_order.rs index ca380f0a..ad90bc20 100644 --- a/interface/src/sign_order.rs +++ b/interface/src/sign_order.rs @@ -5,10 +5,10 @@ use zklink_sdk_signers::zklink_signer::pk_signer::ZkLinkSigner; use zklink_sdk_types::tx_type::order_matching::Order; use zklink_sdk_types::tx_type::TxTrait; #[cfg(not(feature = "ffi"))] -pub fn sign(order: &mut Order, signer: &ZkLinkSigner) -> Result<(), ZkSignerError> { - let bytes = order.get_bytes(); - order.signature = signer.sign_musig(&bytes)?; - Ok(()) +pub fn sign_order(order: &Order, zklink_signer: &ZkLinkSigner) -> Result { + let mut order = order.clone(); + order.signature = zklink_signer.sign_musig(&order.get_bytes())?; + Ok(order) } #[cfg(feature = "ffi")] diff --git a/interface/src/signer.rs b/interface/src/signer.rs index 2babbaa7..c39ae236 100644 --- a/interface/src/signer.rs +++ b/interface/src/signer.rs @@ -1,5 +1,6 @@ use crate::error::SignError; use crate::sign_forced_exit::sign_forced_exit; +use crate::sign_order::sign_order; use crate::sign_order_matching::sign_order_matching; use crate::sign_transfer::sign_transfer; use crate::sign_withdraw::sign_withdraw; @@ -15,7 +16,7 @@ use zklink_sdk_signers::zklink_signer::signature::ZkLinkSignature; use zklink_sdk_types::basic_types::ZkLinkAddress; use zklink_sdk_types::tx_type::change_pubkey::{ChangePubKey, ChangePubKeyAuthData, Create2Data}; use zklink_sdk_types::tx_type::forced_exit::ForcedExit; -use zklink_sdk_types::tx_type::order_matching::OrderMatching; +use zklink_sdk_types::tx_type::order_matching::{Order, OrderMatching}; use zklink_sdk_types::tx_type::transfer::Transfer; use zklink_sdk_types::tx_type::withdraw::Withdraw; use zklink_sdk_types::tx_type::zklink_tx::ZkLinkTx; @@ -215,6 +216,12 @@ impl Signer { Ok(signature) } + #[cfg(not(feature = "ffi"))] + pub fn sign_order(&self, order: &Order) -> Result { + let signed_order = sign_order(order, &self.zklink_signer)?; + Ok(signed_order) + } + #[cfg(feature = "ffi")] pub fn sign_order_matching(&self, tx: Arc) -> Result { let tx = (*tx).clone(); diff --git a/provider/src/error.rs b/provider/src/error.rs index c676958d..fdc814c0 100644 --- a/provider/src/error.rs +++ b/provider/src/error.rs @@ -14,8 +14,10 @@ pub enum RpcError { RequestError(reqwest::Error), #[error("Parse response error: {0}")] ResponseError(reqwest::Error), - #[error("Parse json value error")] - ParseJsonError, + #[error("Parse json value error: {0}")] + ParseJsonError(String), + #[error("Get error result: {0}")] + GetErrorResult(String), } impl From for JsValue { diff --git a/provider/src/lib.rs b/provider/src/lib.rs index 56f1f87a..afd32117 100644 --- a/provider/src/lib.rs +++ b/provider/src/lib.rs @@ -1,12 +1,10 @@ pub mod error; pub mod network; pub mod response; -#[cfg(not(feature = "ffi"))] -#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(any(feature = "ffi", target_arch = "wasm32")))] pub mod rpc; -#[cfg(not(feature = "ffi"))] -#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(any(feature = "ffi", target_arch = "wasm32")))] mod not_ffi { use crate::network::Network; use jsonrpsee::http_client::{HttpClient, HttpClientBuilder}; @@ -38,12 +36,9 @@ mod not_ffi { } } -#[cfg(not(feature = "ffi"))] -#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(any(feature = "ffi", target_arch = "wasm32")))] pub use crate::rpc::ZkLinkRpcClient; -#[cfg(not(feature = "ffi"))] -#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(any(feature = "ffi", target_arch = "wasm32")))] pub use jsonrpsee::core::Error as RpcError; -#[cfg(not(feature = "ffi"))] -#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(any(feature = "ffi", target_arch = "wasm32")))] pub use not_ffi::*; diff --git a/provider/tests/test_rpc.rs b/provider/tests/test_rpc.rs index 3c53348c..de1ec6f7 100644 --- a/provider/tests/test_rpc.rs +++ b/provider/tests/test_rpc.rs @@ -9,10 +9,11 @@ mod test { use zklink_sdk_types::basic_types::{ AccountId, ChainId, Nonce, SubAccountId, TimeStamp, TokenId, ZkLinkAddress, }; - use zklink_sdk_types::tx_builder::ChangePubKeyBuilder; + use zklink_sdk_types::tx_builder::{ChangePubKeyBuilder, OrderMatchingBuilder}; use zklink_sdk_types::tx_type::change_pubkey::ChangePubKey; + use zklink_sdk_types::tx_type::order_matching::{Order, OrderMatching}; use zklink_sdk_types::tx_type::zklink_tx::ZkLinkTx; - use zklink_sdk_types::tx_type::ZkSignatureTrait; + use zklink_sdk_types::tx_type::{TxTrait, ZkSignatureTrait}; #[tokio::test] async fn test_send_change_pubkey() { @@ -70,8 +71,70 @@ mod test { None, Some(submitter_signature), ) - .await + .await; + println!("{:?}", ret) + } + + #[tokio::test] + async fn test_send_order_matching() { + let private_key = "be725250b123a39dab5b7579334d5888987c72a58f4508062545fe6e08ca94f4"; + let zklink_signer = ZkLinkSigner::new_from_hex_eth_signer(private_key).unwrap(); + let maker_order = Order::new( + 5.into(), + 1.into(), + 1.into(), + 1.into(), + 18.into(), + 17.into(), + BigUint::from_str("10000000000000").unwrap(), + BigUint::from_str("10000000000").unwrap(), + true, + 5, + 3, + ); + let mut maker = maker_order.clone(); + maker.signature = zklink_signer.sign_musig(&maker_order.get_bytes()).unwrap(); + let taker_order = Order::new( + 5.into(), + 1.into(), + 1.into(), + 1.into(), + 18.into(), + 17.into(), + BigUint::from_str("10000000000000").unwrap(), + BigUint::from_str("10000000000").unwrap(), + false, + 5, + 3, + ); + let mut taker = taker_order.clone(); + taker.signature = zklink_signer.sign_musig(&taker_order.get_bytes()).unwrap(); + //auth type 'ECDSA' + let builder = OrderMatchingBuilder { + account_id: AccountId(10), + sub_account_id: SubAccountId(1), + taker, + fee_token: TokenId(18), + expect_base_amount: BigUint::from(10000000000000000u64), + fee: BigUint::from(100000000000000u64), + maker, + expect_quote_amount: BigUint::from(100000000000000u64), + }; + let mut order_matching = OrderMatching::new(builder); + order_matching.sign(&zklink_signer).unwrap(); + let submitter_signature = order_matching.submitter_signature(&zklink_signer).unwrap(); + + //use jsonrpsee + let client = HttpClientBuilder::default() + .build("https://aws-gw-v2.zk.link") .unwrap(); + let ret = client + .tx_submit( + ZkLinkTx::OrderMatching(Box::new(order_matching.clone())), + None, + Some(submitter_signature), + ) + .await; println!("{:?}", ret) } }