-
Notifications
You must be signed in to change notification settings - Fork 11
Description
Background
near-api-rs currently depends on two crates from near-openapi-client-rs:
near-openapi-client— the HTTP client (progenitor-generated from OpenAPI spec)near-openapi-types— all RPC types
An alternative exists: near-openrpc-client-rs, which is generated from the OpenRPC spec using typify. It has several advantages that would fix current pain points.
Why consider migrating
1. Better type names and enum variants
The openapi-generated types use opaque Variant0/Variant1 names for untagged enum variants, making code hard to read and fragile:
// openapi (current)
RpcTransactionResponse::Variant0 { ... }
RpcTransactionResponse::Variant1 { ... }
JsonRpcResponseForRpcQueryResponseAndRpcQueryError::Variant0 { result, .. }
// openrpc (proposed)
RpcTransactionResponse::FinalExecutionOutcomeWithReceiptView { ... }
RpcTransactionResponse::FinalExecutionOutcomeView { ... }
RpcTransactionResponse::Empty { final_execution_status }2. Already handles NONE/INCLUDED transaction responses
The openrpc client's RpcTransactionResponse has an Empty variant that correctly handles send_tx with wait_until: None or Included. The openapi client is missing this variant entirely, which causes deserialization failures (see near-openapi-client-rs#40 and #117).
3. Dedicated EXPERIMENTAL_* endpoints
The openrpc client already has dedicated methods (view_account, view_code, view_state, view_access_key, view_access_key_list, call_function) that call the EXPERIMENTAL_* endpoints directly. This aligns with the goal in #122 to migrate away from the multiplexed query endpoint. Currently, near-api-rs has a complex QueryRequest enum in api/src/common/query/query_request.rs that manually constructs all the ByFinality/ByBlockId variants — this would be replaced by the openrpc client's cleaner request types.
4. Simpler error types
The openrpc client uses a straightforward Error enum (Http, Rpc, Json) with a structured RpcError that exposes name, cause, and cause_name(). The openapi client's error chain involves ErrorWrapperForRpc*Error types with InternalError/RequestValidationError/HandlerError variants that need manual From impls for each RPC method.
5. Simpler client architecture
The openrpc client directly constructs JSON-RPC requests and posts them, whereas the openapi client wraps everything through a progenitor-generated HTTP layer that adds indirection (e.g., requiring JsonRpcRequestForQuery wrappers with id, jsonrpc, method fields to be manually constructed by the consumer).
Type mapping
Types re-exported from near_openapi_types in types/src/lib.rs
near_openapi_types |
near_openrpc_client::types |
Notes |
|---|---|---|
AccountView |
RpcViewAccountResponse |
Same fields, different name |
ContractCodeView |
RpcViewCodeResponse |
Same fields, different name |
FunctionArgs |
FunctionArgs |
Same |
RpcBlockResponse |
RpcBlockResponse |
Same |
RpcTransactionResponse |
RpcTransactionResponse |
openrpc adds Empty variant |
RpcValidatorResponse |
RpcValidatorResponse |
Same |
StoreKey |
StoreKey |
Same |
StoreValue |
StoreValue |
Same |
TxExecutionStatus |
TxExecutionStatus |
Same |
ViewStateResult |
RpcViewStateResponse |
Same fields, different name |
CallResult |
RpcCallFunctionResponse |
Same fields, different name |
Types used for From/TryFrom conversions in types/src/
All of these exist in both crates with matching fields:
DelegateAction, NonDelegateAction, SignedDelegateAction, SignedTransactionView, ExecutionOutcomeWithIdView, ActionView, AccessKeyView, AccessKey, AccessKeyPermission, AccessKeyPermissionView, FunctionCallPermission, PublicKey, CryptoHash, FinalExecutionOutcomeView, FinalExecutionStatus, ExecutionStatusView, TxExecutionError, AccountView (→ RpcViewAccountResponse), and all action types (CreateAccountAction, DeployContractAction, FunctionCallAction, TransferAction, StakeAction, AddKeyAction, DeleteKeyAction, DeleteAccountAction, DelegateAction, DeployGlobalContractAction, UseGlobalContractAction, AddGasKeyAction, DeleteGasKeyAction, TransferToGasKeyAction, DeterministicStateInitAction, GlobalContractDeployMode, GlobalContractIdentifier, GlobalContractIdentifierView).
Client types used in api/src/
near_openapi_client |
near_openrpc_client |
Notes |
|---|---|---|
Client (progenitor HTTP client) |
NearRpcClient |
Different interface |
Client::new_with_client(url, reqwest_client) |
NearRpcClient::new(url) |
Gap: no custom reqwest::Client support |
client.query(...) |
client.view_account(...), client.call_function(...), etc. |
Dedicated methods instead of multiplexed |
client.send_tx(...) |
client.send_tx(...) |
Same |
client.block(...) |
client.block(...) |
Same |
client.validators(...) |
client.validators(...) |
Same |
Error::ErrorResponse, Error::InvalidRequest, etc. |
Error::Http, Error::Rpc, Error::Json |
Simpler error model |
Types that exist in openapi but NOT in openrpc
| Type | Used in | Notes |
|---|---|---|
RpcQueryRequest (multiplexed enum) |
query_request.rs |
Not needed — openrpc uses dedicated request types per method |
RpcQueryResponse (union type) |
query_rpc.rs, handlers/ |
Not needed — openrpc returns typed responses per method |
RpcQueryError |
utils.rs |
Replaced by generic RpcError with cause_name() |
ErrorWrapperForRpc*Error |
block_rpc.rs, query_rpc.rs, etc. |
Not needed — simpler error model |
JsonRpcRequestFor* / JsonRpcResponseFor* wrappers |
All RPC call sites | Not needed — client handles JSON-RPC framing internally |
Gaps to address in near-openrpc-client-rs
-
Custom
reqwest::Clientsupport —near-api-rsneeds this for auth headers (Bearer tokens, API keys) and custom timeouts.NearRpcClientcurrently only accepts a URL string. Anew_with_client(url, reqwest::Client)constructor needs to be added. -
Re-exported type aliases — Some types like
AccountView,ContractCodeView,CallResult,ViewStateResulthave different names in openrpc (RpcViewAccountResponse,RpcViewCodeResponse,RpcCallFunctionResponse,RpcViewStateResponse). Either add type aliases in openrpc or updatenear-api-rsto use the new names.
Effort estimate
Medium — mostly mechanical renaming, but with some structural changes:
- types/src/: Update ~30
From/TryFromimpls to referencenear_openrpc_client::types::instead ofnear_openapi_types::. Most type shapes match, so this is renaming. A few types likeAccountView→RpcViewAccountResponseneed name changes in the public API. - api/src/common/query/: This is the biggest change. The
QueryRequestenum andquery_rpc.rscan be significantly simplified by using the openrpc client's dedicated endpoints directly (e.g.,client.view_account(RpcViewAccountRequest::FinalityAccountId { ... })). TheSimpleQueryRpctrait impl andErrorWrapperForRpc*conversions can be dropped. - api/src/common/send.rs: Update
send_txto use the openrpc client directly. TheRpcTransactionResponsehandling gets cleaner with named variants +Empty. - api/src/config.rs: Update
RPCEndpoint::client()to constructNearRpcClientinstead ofnear_openapi_client::Client. Requires addingnew_with_clientto openrpc first. - api/src/errors.rs: Simplify error mapping from openrpc's
Errorenum. - api/src/common/utils.rs: Update retry/critical-error logic. The
is_critical_*functions need to useRpcError::cause_name()instead of matching on typed error enums. - Cargo.toml: Swap
near-openapi-client/near-openapi-typesdeps fornear-openrpc-client.
Estimated at ~2-4 days of work for a developer familiar with the codebase, including testing.
Related issues
- near-openapi-client-rs#40 —
RpcTransactionResponsemissingEmptyvariant (fixed in openrpc) - #117 — Error when sending a transaction (caused by the missing variant)
- #122 — Migrate away from
queryendpoint to dedicatedEXPERIMENTAL_*endpoints (natural fit for openrpc's API)
Metadata
Metadata
Assignees
Labels
Type
Projects
Status