From c860c9bf3c0a134e65b868f01f3e4a6f5b66575e Mon Sep 17 00:00:00 2001 From: Nicolas Villanueva Date: Fri, 11 Aug 2023 19:02:06 +0100 Subject: [PATCH] feat: implement zks_getTokenPrice endpoint (#58) --- SUPPORTED_APIS.md | 109 ++++++++++++++++++++++++++++++++++++-------- src/zks.rs | 86 +++++++++++++++++++++++++++++++++- test_endpoints.http | 11 +++++ 3 files changed, 184 insertions(+), 22 deletions(-) diff --git a/SUPPORTED_APIS.md b/SUPPORTED_APIS.md index 36c0d243..3d85c832 100644 --- a/SUPPORTED_APIS.md +++ b/SUPPORTED_APIS.md @@ -92,6 +92,8 @@ | `ETH` | `eth_uninstallFilter` | NOT IMPLEMENTED | Uninstalls a filter with given id | | `ETH` | `eth_accounts` | NOT IMPLEMENTED | Returns a list of addresses owned by client | | `ETH` | `eth_unsubscribe` | NOT IMPLEMENTED | Cancel a subscription to a particular event | +| [`ZKS`](#zks-namespace) | [`zks_estimateFee`](#zks_estimateFee) | SUPPORTED | Gets the Fee estimation data for a given Request | +| [`ZKS`](#zks-namespace) | [`zks_getTokenPrice`](#zks_getTokenPrice) | SUPPORTED | Gets the USD price of a token | ## Key @@ -176,7 +178,7 @@ curl --request POST \ ### `net_version` -[source](src/network_api.rs) +[source](src/net.rs) Returns the current network id @@ -199,7 +201,7 @@ curl --request POST \ ### `net_peerCount` -[source](src/network_api.rs) +[source](src/net.rs) Returns the number of connected peers @@ -222,7 +224,7 @@ curl --request POST \ ### `net_listening` -[source](src/network_api.rs) +[source](src/net.rs) Returns `true` if the node is listening for connections @@ -247,7 +249,7 @@ curl --request POST \ ### `eth_chainId` -[source](src/eth_api.rs) +[source](src/node.rs) Returns the current chain id @@ -270,7 +272,7 @@ curl --request POST \ ### `eth_estimateGas` -[source](src/eth_api.rs) +[source](src/node.rs) Generates and returns an estimate of how much gas is necessary to allow the transaction to complete @@ -280,7 +282,7 @@ Generates and returns an estimate of how much gas is necessary to allow the tran #### Status -`PARTIALLY` +`SUPPORTED` #### Example @@ -290,17 +292,23 @@ curl --request POST \ --header 'content-type: application/json' \ --data '{ "jsonrpc": "2.0", - "id": "1", - "method": "eth_estimateGas", - "params": [{ - "0x0000000000000000000000000000000000000000": true - }] + "id": "2", + "method": "eth_estimateGas", + "params": [{ + "to": "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049", + "data": "0x0000", + "from": "0xa61464658AfeAf65CccaaFD3a512b69A83B77618", + "gas": "0x0000", + "gasPrice": "0x0000", + "value": "0x0000", + "nonce": "0x0000" + }, "latest"] }' ``` ### `eth_gasPrice` -[source](src/eth_api.rs) +[source](src/node.rs) Returns the current price per gas in wei @@ -323,7 +331,7 @@ curl --request POST \ ### `eth_getBalance` -[source](src/eth_api.rs) +[source](src/node.rs) Returns the balance of the account of given address @@ -353,7 +361,7 @@ curl --request POST \ ### `eth_getBlockByNumber` -[source](src/eth_api.rs) +[source](src/node.rs) Returns information about a block by block number @@ -383,7 +391,7 @@ curl --request POST \ ### `eth_getCode` -[source](src/eth_api.rs) +[source](src/node.rs) Returns code at a given address @@ -413,7 +421,7 @@ curl --request POST \ ### `eth_getTransactionByHash` -[source](src/eth_api.rs) +[source](src/node.rs) Returns the information about a transaction requested by transaction hash @@ -441,7 +449,7 @@ curl --request POST \ ### `eth_getTransactionCount` -[source](src/eth_api.rs) +[source](src/node.rs) Returns the number of transactions sent from an address @@ -471,7 +479,7 @@ curl --request POST \ ### `eth_blockNumber` -[source](src/eth_api.rs) +[source](src/node.rs) Returns the number of most recent block @@ -494,7 +502,7 @@ curl --request POST \ ### `eth_call` -[source](src/eth_api.rs) +[source](src/node.rs) Executes a new message call immediately without creating a transaction on the block chain @@ -532,7 +540,7 @@ curl --request POST \ ### `eth_sendRawTransaction` -[source](src/eth_api.rs) +[source](src/node.rs) Creates new message call transaction or a contract creation for signed transactions @@ -553,3 +561,64 @@ curl --request POST \ --data '{"jsonrpc": "2.0","id": "1","method": "eth_sendRawTransaction","params": ["0x0000"] }' ``` + +## `ZKS NAMESPACE` + +### `zks_estimateFee` + +[source](src/zks.rs) + +Generates and returns an estimate of how much gas is necessary to allow the transaction to complete + +#### Arguments + ++ `transaction: Transaction` + +#### Status + +`SUPPORTED` + +#### Example + +```bash +curl --request POST \ + --url http://localhost:8011/ \ + --header 'content-type: application/json' \ + --data '{ + "jsonrpc": "2.0", + "id": "2", + "method": "zks_estimateFee", + "params": [{ + "to": "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049", + "data": "0x0000", + "from": "0xa61464658AfeAf65CccaaFD3a512b69A83B77618", + "gas": "0x0000", + "gasPrice": "0x0000", + "value": "0x0000", + "nonce": "0x0000" + }] + }' +``` + +### `zks_getTokenPrice` + +[source](src/zks.rs) + +Returns the token price given an Address + +#### Arguments + ++ `address: Address` + +#### Status + +`SUPPORTED` + +#### Example + +```bash +curl --request POST \ + --url http://localhost:8011/ \ + --header 'content-type: application/json' \ + --data '{"jsonrpc": "2.0","id": "1","method": "zks_getTokenPrice","params": ["0x0000000000000000000000000000000000000000"]}' +``` diff --git a/src/zks.rs b/src/zks.rs index 75e8714a..2b11ec04 100644 --- a/src/zks.rs +++ b/src/zks.rs @@ -10,6 +10,7 @@ use zksync_types::{api::BridgeAddresses, fee::Fee}; use zksync_web3_decl::error::Web3Error; use crate::{node::InMemoryNodeInner, utils::IntoBoxedFuture}; +use colored::Colorize; /// Mock implementation of ZksNamespace - used only in the test node. pub struct ZkMockNamespaceImpl { @@ -104,9 +105,39 @@ impl ZksNamespaceT for ZkMockNamespaceImpl { fn get_token_price( &self, - _token_address: zksync_basic_types::Address, + token_address: zksync_basic_types::Address, ) -> jsonrpc_core::BoxFuture> { - not_implemented!() + match format!("{:?}", token_address).to_lowercase().as_str() { + "0x0000000000000000000000000000000000000000" => { + // ETH + Ok(1_500.into()).into_boxed_future() + } + "0x40609141db628beee3bfab8034fc2d8278d0cc78" => { + // LINK + Ok(1.into()).into_boxed_future() + } + "0x0bfce1d53451b4a8175dd94e6e029f7d8a701e9c" => { + // wBTC + Ok(1.into()).into_boxed_future() + } + "0x0faf6df7054946141266420b43783387a78d82a9" => { + // USDC + Ok(1.into()).into_boxed_future() + } + "0x3e7676937a7e96cfb7616f255b9ad9ff47363d4b" => { + // DAI + Ok(1.into()).into_boxed_future() + } + address => { + println!( + "{}", + format!("Token price requested for unknown address {:?}", address) + .to_string() + .red() + ); + futures::future::err(into_jsrpc_error(Web3Error::InternalError)).boxed() + } + } } fn get_all_account_balances( @@ -202,9 +233,12 @@ impl ZksNamespaceT for ZkMockNamespaceImpl { #[cfg(test)] mod tests { + use std::str::FromStr; + use crate::node::InMemoryNode; use super::*; + use zksync_basic_types::Address; use zksync_types::transaction_request::CallRequest; #[tokio::test] @@ -242,4 +276,52 @@ mod tests { assert_eq!(result.max_priority_fee_per_gas, U256::from(0)); assert_eq!(result.gas_per_pubdata_limit, U256::from(4080)); } + + #[tokio::test] + async fn test_get_token_price_given_eth_should_return_price() { + // Arrange + let node = InMemoryNode::new(None, crate::ShowCalls::None, false, false); + let namespace = ZkMockNamespaceImpl::new(node.get_inner()); + + let mock_address = Address::from_str("0x0000000000000000000000000000000000000000") + .expect("Failed to parse address"); + + // Act + let result = namespace.get_token_price(mock_address).await.unwrap(); + + // Assert + assert_eq!(result, BigDecimal::from(1_500)); + } + + #[tokio::test] + async fn test_get_token_price_given_capitalized_link_address_should_return_price() { + // Arrange + let node = InMemoryNode::new(None, crate::ShowCalls::None, false, false); + let namespace = ZkMockNamespaceImpl::new(node.get_inner()); + + let mock_address = Address::from_str("0x40609141Db628BeEE3BfAB8034Fc2D8278D0Cc78") + .expect("Failed to parse address"); + + // Act + let result = namespace.get_token_price(mock_address).await.unwrap(); + + // Assert + assert_eq!(result, BigDecimal::from(1)); + } + + #[tokio::test] + async fn test_get_token_price_given_unknown_address_should_return_error() { + // Arrange + let node = InMemoryNode::new(None, crate::ShowCalls::None, false, false); + let namespace = ZkMockNamespaceImpl::new(node.get_inner()); + + let mock_address = Address::from_str("0x0000000000000000000000000000000000000042") + .expect("Failed to parse address"); + + // Act + let result = namespace.get_token_price(mock_address).await; + + // Assert + assert!(result.is_err()); + } } diff --git a/test_endpoints.http b/test_endpoints.http index 51c54910..acfa3832 100644 --- a/test_endpoints.http +++ b/test_endpoints.http @@ -226,4 +226,15 @@ content-type: application/json "value": "0x0000", "nonce": "0x0000" }, "latest"] +} + +### +POST http://localhost:8011 +content-type: application/json + +{ + "jsonrpc": "2.0", + "id": "1", + "method": "zks_getTokenPrice", + "params": ["0x0000000000000000000000000000000000000000"] } \ No newline at end of file