Skip to contents

Introduction

nearjsonrpc provides small, focused wrappers around the NEAR Protocol JSON-RPC API. The goal is to make it easy to query account state, blocks, network status, transactions and call view functions from reproducible R scripts.

Setup

Set the endpoint:

##  NEAR RPC endpoint set to <https://rpc.mainnet.near.org>

Examples

Query Acount Details and Balance

near_query_account("vitalik.near")
## # A tibble: 1 × 8
##   account_id   amount     locked storage_usage code_hash block_height block_hash
##   <chr>        <chr>      <chr>          <int> <chr>            <int> <chr>     
## 1 vitalik.near 546329351… 0             358058 55E7imni…    176808020 C3NMcEbHy…
## # ℹ 1 more variable: raw_response <list>

List all access keys associated with a NEAR account

keys = near_get_access_keys("vitalik.near")
keys
## # A tibble: 7 × 5
##   public_key                   access_key   block_height block_hash raw_response
##   <chr>                        <list>              <int> <chr>      <list>      
## 1 ed25519:5a4CCWCJrnMGxSVtjMp… <named list>    176808021 34GLqdgDj… <named list>
## 2 ed25519:6EBwyCYEjH1VsjJSUQo… <named list>    176808021 34GLqdgDj… <named list>
## 3 ed25519:77oj7oJGG3AoeRaKaAF… <named list>    176808021 34GLqdgDj… <named list>
## 4 ed25519:7CH3JEY5abPhVcdPQxg… <named list>    176808021 34GLqdgDj… <named list>
## 5 ed25519:9ePBRZ3W1RpUKsaErip… <named list>    176808021 34GLqdgDj… <named list>
## 6 ed25519:Dr9NjCcEMvXEjECaJ9t… <named list>    176808021 34GLqdgDj… <named list>
## 7 ed25519:DyijRtwLRrSysLAgEAn… <named list>    176808021 34GLqdgDj… <named list>
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
## 
## Attaching package: 'purrr'
## The following object is masked from 'package:nearjsonrpc':
## 
##     %||%
## 
## Attaching package: 'rlang'
## The following objects are masked from 'package:purrr':
## 
##     %@%, flatten, flatten_chr, flatten_dbl, flatten_int, flatten_lgl,
##     flatten_raw, invoke, splice
## The following object is masked from 'package:nearjsonrpc':
## 
##     %||%
keys = near_get_access_keys("vitalik.near")
library(dplyr)
library(purrr)
library(tidyr)
library(rlang)

# Flatten raw_response into a dataframe
all_keys_df <- map_dfr(keys$raw_response, function(resp) {
  # Some elements may not have $keys
  if (!is.null(resp$keys)) {
    map_dfr(resp$keys, function(k) {
      ak <- k$access_key
      pk <- k$public_key %||% NA_character_
      
      if (is.list(ak) && "FunctionCall" %in% names(ak$permission)) {
        fc <- ak$permission$FunctionCall
        tibble(
          block_hash   = resp$block_hash %||% NA_character_,
          block_height = resp$block_height %||% NA_integer_,
          public_key   = pk,
          nonce        = ak$nonce,
          allowance    = fc$allowance %||% NA_character_,
          method_names = if (length(fc$method_names) > 0) paste(fc$method_names, collapse = ", ") else NA_character_,
          receiver_id  = fc$receiver_id,
          permission_type = "FunctionCall"
        )
      } else {
        tibble(
          block_hash   = resp$block_hash %||% NA_character_,
          block_height = resp$block_height %||% NA_integer_,
          public_key   = pk,
          nonce        = ak$nonce,
          allowance    = NA_character_,
          method_names = NA_character_,
          receiver_id  = NA_character_,
          permission_type = "FullAccess"
        )
      }
    })
  } else {
    tibble(
      block_hash   = NA_character_,
      block_height = NA_integer_,
      public_key   = NA_character_,
      nonce        = NA_integer_,
      allowance    = NA_character_,
      method_names = NA_character_,
      receiver_id  = NA_character_,
      permission_type = NA_character_
    )
  }
})

all_keys_df
## # A tibble: 49 × 8
##    block_hash block_height public_key   nonce allowance method_names receiver_id
##    <chr>             <int> <chr>        <dbl> <chr>     <chr>        <chr>      
##  1 GMgABe9f8…    176808022 ed25519:5… 1.4 e 1 23318196… add_request… vitalik.ne…
##  2 GMgABe9f8…    176808022 ed25519:6… 1.5 e 1 NA        add_request… vitalik.ne…
##  3 GMgABe9f8…    176808022 ed25519:7… 0       25000000… NA           claim-toke…
##  4 GMgABe9f8…    176808022 ed25519:7… 5.24e13 25000000… NA           launchpad.…
##  5 GMgABe9f8…    176808022 ed25519:9… 3.1 e 1 NA        confirm      vitalik.ne…
##  6 GMgABe9f8…    176808022 ed25519:D… 5.24e13 24755542… add_request… vitalik.ne…
##  7 GMgABe9f8…    176808022 ed25519:D… 3   e 0 NA        NA           NA         
##  8 GMgABe9f8…    176808022 ed25519:5… 1.4 e 1 23318196… add_request… vitalik.ne…
##  9 GMgABe9f8…    176808022 ed25519:6… 1.5 e 1 NA        add_request… vitalik.ne…
## 10 GMgABe9f8…    176808022 ed25519:7… 0       25000000… NA           claim-toke…
## # ℹ 39 more rows
## # ℹ 1 more variable: permission_type <chr>

Get Current Network Status and Node Info

status = near_network_status()
status
## # A tibble: 1 × 8
##   chain_id latest_block_height latest_block_hash                 syncing version
##   <chr>                  <int> <chr>                             <lgl>   <chr>  
## 1 mainnet            176808025 5jTZfcko6FxGzcYrrZaeammaW3genXCF… FALSE   2.10.2 
## # ℹ 3 more variables: protocol_version <int>, validators <list>,
## #   raw_response <list>

Get NEAR Protocol Configuration

protocol_config = near_get_protocol_config()
protocol_config
## # A tibble: 1 × 5
##   block_height block_hash config            runtime_config   raw_response     
##          <int> <chr>      <list>            <list>           <list>           
## 1           NA NA         <named list [33]> <named list [6]> <named list [33]>

View NEAR Smart Contract

FT Metadata

near_call_view_function("wrap.near", "ft_metadata")
## # A tibble: 1 × 9
##   account_id method_name result_raw result_text result_json  logs   block_height
##   <chr>      <chr>       <list>     <chr>       <list>       <list>        <int>
## 1 wrap.near  ft_metadata <raw>      "{\"spec\"… <named list> <list>    176808024
## # ℹ 2 more variables: block_hash <chr>, raw_response <list>

Total supply

near_call_view_function("wrap.near", "ft_total_supply")
## # A tibble: 1 × 9
##   account_id method_name  result_raw result_text result_json logs   block_height
##   <chr>      <chr>        <list>     <chr>       <list>      <list>        <int>
## 1 wrap.near  ft_total_su… <raw [34]> "\"1880877… <chr [1]>   <list>    176808024
## # ℹ 2 more variables: block_hash <chr>, raw_response <list>

Balance query

near_call_view_function("wrap.near", "ft_balance_of",
                        args = list(account_id = "vitalik.near"))
## # A tibble: 1 × 9
##   account_id method_name  result_raw result_text result_json logs   block_height
##   <chr>      <chr>        <list>     <chr>       <list>      <list>        <int>
## 1 wrap.near  ft_balance_… <raw [3]>  "\"0\""     <chr [1]>   <list>    176808025
## # ℹ 2 more variables: block_hash <chr>, raw_response <list>

Data analysis

Top validators based on stakes

## 
## Attaching package: 'scales'
## The following object is masked from 'package:purrr':
## 
##     discard
# Get recent validator stakes
vals <- near_get_validators()

vals %>%
  slice_head(n = 10) %>%
  ggplot(aes(x = reorder(account_id, stake_near), y = stake_near)) +
  geom_col(fill = "steelblue") +
  coord_flip() +
  scale_y_continuous(labels = comma) +   # makes stake readable with commas
  labs(
    title = "Top 10 Validators by Stake",
    x = "Validator",
    y = "Stake (NEAR)"
  ) +
  theme_minimal()

Distribution of States (Major of the validators few Stakes)

vals %>%
  ggplot(aes(x = stake_near)) +
  geom_histogram(bins = 30, fill = "darkgreen", color = "white") +
  scale_x_continuous(labels = scales::comma) +
  labs(
    title = "Distribution of Validator Stakes",
    x = "Stake (NEAR)",
    y = "Number of Validators"
  ) +
  theme_minimal()

Stake Share by Validator (Pie Chart)

vals %>%
  slice_max(order_by = stake_near, n = 10) %>%
  ggplot(aes(x = "", y = stake_near, fill = account_id)) +
  geom_col(width = 1) +
  coord_polar(theta = "y") +
  scale_y_continuous(labels = scales::comma) +
  labs(
    title = "Top 10 Validators Stake Share",
    fill = "Validator"
  ) +
  theme_void()

Concentration Index (Lorenz Curve)

Show how concentrated stake is among validators

vals %>%
  arrange(desc(stake_near)) %>%
  mutate(cum_stake = cumsum(stake_near) / sum(stake_near),
         cum_validators = row_number() / n()) %>%
  ggplot(aes(x = cum_validators, y = cum_stake)) +
  geom_line(color = "gold", size = 1.2) +
  geom_abline(slope = 1, intercept = 0, linetype = "dashed") +
  labs(
    title = "Lorenz Curve of Validator Stakes",
    x = "Cumulative Share of Validators",
    y = "Cumulative Share of Stake"
  ) +
  theme_minimal()
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
##  Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

Bar Plot of Stake Concentration

vals %>%
  arrange(desc(stake_near)) %>%
  mutate(cum_share = cumsum(stake_near) / sum(stake_near)) %>%
  slice_head(n = 20) %>%
  ggplot(aes(x = reorder(account_id, cum_share), y = cum_share)) +
  geom_col(fill = "orange") +
  coord_flip() +
  scale_y_continuous(labels = scales::percent) +
  labs(
    title = "Cumulative Stake Share of Top 20 Validators",
    x = "Validator",
    y = "Cumulative Stake Share"
  ) +
  theme_minimal()

The result indicates that validator stakes are highly concentrated. A small number of validators control the majority of the total stake. The network is therefore less decentralized in terms of stake distribution.

Get Block Information

Latest block

## # A tibble: 1 × 10
##      height hash  prev_hash timestamp           author chunks_included gas_price
##       <int> <chr> <chr>     <dttm>              <chr>            <int> <chr>    
## 1 176808033 GJB1… 9iN7BSQb… 2025-12-14 13:47:35 epoch…               9 100000000
## # ℹ 3 more variables: total_supply <chr>, latest_protocol_version <int>,
## #   raw_response <list>

Block by hash

near_get_block("CanDKa6nYDQ89iv5U7sE1YDFteBRkBG6qBJjiM5YwGtY")
## # A tibble: 0 × 10
## # ℹ 10 variables: height <int>, hash <chr>, prev_hash <chr>, timestamp <dttm>,
## #   author <chr>, chunks_included <int>, gas_price <chr>, total_supply <chr>,
## #   latest_protocol_version <int>, raw_response <list>

Error handling and best practices

  • Avoid embedding real keys or signed transactions in vignettes or tests.

Contributing

Please see the repository on GitHub. Open issues or PRs for improvements.