Skip to content

Commit

Permalink
feat: impl zks_getAllAccountBalances and zks_getConfirmedTokens (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
grw-ms authored Oct 25, 2023
1 parent 1ed74ea commit c8fa0b1
Show file tree
Hide file tree
Showing 7 changed files with 439 additions and 16 deletions.
61 changes: 59 additions & 2 deletions SUPPORTED_APIS.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,11 @@ The `status` options are:
| [`NETWORK`](#network-namespace) | [`net_listening`](#net_listening) | `SUPPORTED` | Returns `true` if the client is actively listening for network connections <br />_(hard-coded to `false`)_ |
| [`ZKS`](#zks-namespace) | [`zks_estimateFee`](#zks_estimateFee) | `SUPPORTED` | Gets the Fee estimation data for a given Request |
| `ZKS` | `zks_estimateGasL1ToL2` | `NOT IMPLEMENTED` | Estimate of the gas required for a L1 to L2 transaction |
| `ZKS` | `zks_getAllAccountBalances` | `NOT IMPLEMENTED` | Returns all balances for confirmed tokens given by an account address |
| [`ZKS`](#zks-namespace) | [`zks_getAllAccountBalances`](#zks_getallaccountbalances) | `SUPPORTED` | Returns all balances for confirmed tokens given by an account address |
| [`ZKS`](#zks-namespace) | [`zks_getBridgeContracts`](#zks_getbridgecontracts) | `SUPPORTED` | Returns L1/L2 addresses of default bridges |
| [`ZKS`](#zks-namespace) | [`zks_getBlockDetails`](#zks_getblockdetails) | `SUPPORTED` | Returns additional zkSync-specific information about the L2 block |
| `ZKS` | `zks_getBytecodeByHash` | `NOT IMPLEMENTED` | Returns bytecode of a transaction given by its hash |
| `ZKS` | `zks_getConfirmedTokens` | `NOT IMPLEMENTED` | Returns [address, symbol, name, and decimal] information of all tokens within a range of ids given by parameters `from` and `limit` |
| [`ZKS`](#zks-namespace) | [`zks_getConfirmedTokens`](#zks_getconfirmedtokens) | `SUPPORTED` | Returns [address, symbol, name, and decimal] information of all tokens within a range of ids given by parameters `from` and `limit` |
| `ZKS` | `zks_getL1BatchBlockRange` | `NOT IMPLEMENTED` | Returns the range of blocks contained within a batch given by batch number |
| `ZKS` | `zks_getL1BatchDetails` | `NOT IMPLEMENTED` | Returns data pertaining to a given batch |
| `ZKS` | `zks_getL2ToL1LogProof` | `NOT IMPLEMENTED` | Given a transaction hash, and an index of the L2 to L1 log produced within the transaction, it returns the proof for the corresponding L2 to L1 log |
Expand Down Expand Up @@ -1864,3 +1864,60 @@ curl --request POST \
--header 'content-type: application/json' \
--data '{"jsonrpc": "2.0", "id": 1, "method": "zks_getRawBlockTransactions", "params": [ 140599 ]}'
```

### `zks_getConfirmedTokens`

[source](src/zks.rs)

Get list of the tokens supported by ZkSync Era. The tokens are returned in alphabetical order by their symbol. This means that the token id is its position in an alphabetically sorted array of tokens.

#### Arguments

+ `from: u32` - Offset of tokens
+ `limit: u8` - Limit of number of tokens to return

#### Status

`SUPPORTED`

#### Example

```bash
curl --request POST \
--url http://localhost:8011/ \
--header 'content-type: application/json' \
--data '{
"jsonrpc": "2.0",
"id": "1",
"method": "zks_getConfirmedTokens",
"params": [0, 100]
}'
```

### `zks_getAllAccountBalances`

[source](src/zks.rs)

Get all known balances for a given account.

#### Arguments

+ `address: Address` - The user address with balances to check.

#### Status

`SUPPORTED`

#### Example

```bash
curl --request POST \
--url http://localhost:8011/ \
--header 'content-type: application/json' \
--data '{
"jsonrpc": "2.0",
"id": "1",
"method": "zks_getAllAccountBalances",
"params": ["0x364d6D0333432C3Ac016Ca832fb8594A8cE43Ca6"]
}'
```
27 changes: 26 additions & 1 deletion e2e-tests/test/zks-apis.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { expect } from "chai";
import { deployContract, getTestProvider } from "../helpers/utils";
import { Wallet } from "zksync-web3";
import { RichAccounts } from "../helpers/constants";
import { ethers } from "ethers";
import { BigNumber, ethers } from "ethers";
import * as hre from "hardhat";
import { TransactionRequest } from "zksync-web3/build/src/types";
import { Deployer } from "@matterlabs/hardhat-zksync-deploy";
Expand Down Expand Up @@ -152,3 +152,28 @@ describe("zks_getRawBlockTransactions", function () {
expect(txns[0]["execute"]["calldata"]).to.equal(receipt.data);
});
});

describe("zks_getConfirmedTokens", function () {
it("Should return only Ether", async function () {
const tokens = await provider.send("zks_getConfirmedTokens", [0, 100]);
expect(tokens.length).to.equal(1);
expect(tokens[0].name).to.equal("Ether");
});
});

describe("zks_getAllAccountBalances", function () {
it("Should return balance of a rich account", async function () {
// Arrange
const account = RichAccounts[0].Account;
const expectedBalance = ethers.utils.parseEther("1000000000000"); // 1_000_000_000_000 ETH
const ethAddress = "0x000000000000000000000000000000000000800a";
await provider.send("hardhat_setBalance", [account, expectedBalance._hex]);

// Act
const balances = await provider.send("zks_getAllAccountBalances", [account]);
const ethBalance = BigNumber.from(balances[ethAddress]);

// Assert
expect(ethBalance.eq(expectedBalance)).to.be.true;
});
});
27 changes: 27 additions & 0 deletions src/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ pub(crate) struct Cache {
block_raw_transactions: FxHashMap<u64, Vec<RawTransaction>>,
transactions: FxHashMap<H256, Transaction>,
bridge_addresses: Option<BridgeAddresses>,
confirmed_tokens: FxHashMap<(u32, u8), Vec<zksync_web3_decl::types::Token>>,
}

impl Cache {
Expand Down Expand Up @@ -158,6 +159,32 @@ impl Cache {
self.block_raw_transactions.get(number)
}

/// Returns the cached confirmed tokens.
pub(crate) fn get_confirmed_tokens(
&self,
from: u32,
limit: u8,
) -> Option<&Vec<zksync_web3_decl::types::Token>> {
if matches!(self.config, CacheConfig::None) {
return None;
}
self.confirmed_tokens.get(&(from, limit))
}

/// Cache confirmed tokens
pub(crate) fn set_confirmed_tokens(
&mut self,
from: u32,
limit: u8,
confirmed_tokens: Vec<zksync_web3_decl::types::Token>,
) {
if matches!(self.config, CacheConfig::None) {
return;
}
self.confirmed_tokens
.insert((from, limit), confirmed_tokens);
}

/// Cache the raw transactions for the provided block number.
pub(crate) fn insert_block_raw_transactions(
&mut self,
Expand Down
7 changes: 7 additions & 0 deletions src/fork.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,13 @@ pub trait ForkSource {

/// Returns addresses of the default bridge contracts.
fn get_bridge_contracts(&self) -> eyre::Result<BridgeAddresses>;

/// Returns confirmed tokens
fn get_confirmed_tokens(
&self,
from: u32,
limit: u8,
) -> eyre::Result<Vec<zksync_web3_decl::types::Token>>;
}

/// Holds the information about the original chain.
Expand Down
80 changes: 75 additions & 5 deletions src/http_fork_source.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
use std::sync::RwLock;

use crate::{
cache::{Cache, CacheConfig},
fork::{block_on, ForkSource},
};
use eyre::Context;
use zksync_basic_types::{H256, U256};
use zksync_types::api::{BridgeAddresses, Transaction};
use zksync_web3_decl::types::Token;
use zksync_web3_decl::{
jsonrpsee::http_client::{HttpClient, HttpClientBuilder},
namespaces::{EthNamespaceClient, ZksNamespaceClient},
types::Index,
};

use crate::{
cache::{Cache, CacheConfig},
fork::{block_on, ForkSource},
};

#[derive(Debug)]
/// Fork source that gets the data via HTTP requests.
pub struct HttpForkSource {
Expand Down Expand Up @@ -306,6 +306,37 @@ impl ForkSource for HttpForkSource {
})
.wrap_err("fork http client failed")
}

/// Returns known token addresses
fn get_confirmed_tokens(&self, from: u32, limit: u8) -> eyre::Result<Vec<Token>> {
if let Some(confirmed_tokens) = self
.cache
.read()
.ok()
.and_then(|guard| guard.get_confirmed_tokens(from, limit).cloned())
{
tracing::debug!("using cached confirmed_tokens");
return Ok(confirmed_tokens);
};

let client = self.create_client();
block_on(async move { client.get_confirmed_tokens(from, limit).await })
.map(|confirmed_tokens| {
self.cache
.write()
.map(|mut guard| {
guard.set_confirmed_tokens(from, limit, confirmed_tokens.clone())
})
.unwrap_or_else(|err| {
tracing::warn!(
"failed writing to cache for 'set_confirmed_tokens': {:?}",
err
)
});
confirmed_tokens
})
.wrap_err("fork http client failed")
}
}

#[cfg(test)]
Expand Down Expand Up @@ -688,4 +719,43 @@ mod tests {
.expect("failed fetching bridge addresses");
testing::assert_bridge_addresses_eq(&input_bridge_addresses, &actual_bridge_addresses);
}

#[test]
fn test_get_confirmed_tokens_is_cached() {
let mock_server = testing::MockServer::run();
mock_server.expect(
serde_json::json!({
"jsonrpc": "2.0",
"id": 0,
"method": "zks_getConfirmedTokens",
"params": [0, 100]
}),
serde_json::json!({
"jsonrpc": "2.0",
"result": [
{
"decimals": 18,
"l1Address": "0xbe9895146f7af43049ca1c1ae358b0541ea49704",
"l2Address": "0x75af292c1c9a37b3ea2e6041168b4e48875b9ed5",
"name": "Coinbase Wrapped Staked ETH",
"symbol": "cbETH"
}
],
"id": 0
}),
);

let fork_source = HttpForkSource::new(mock_server.url(), CacheConfig::Memory);

let tokens = fork_source
.get_confirmed_tokens(0, 100)
.expect("failed fetching tokens");
assert_eq!(tokens.len(), 1);
assert_eq!(tokens[0].symbol, "cbETH");

let tokens = fork_source
.get_confirmed_tokens(0, 100)
.expect("failed fetching tokens");
assert_eq!(tokens.len(), 1);
}
}
Loading

0 comments on commit c8fa0b1

Please sign in to comment.