near_kit/lib.rs
1//! # near-kit
2//!
3//! A clean, ergonomic Rust client for NEAR Protocol.
4//!
5//! `near-kit` provides a fluent API for interacting with NEAR Protocol,
6//! with a focus on developer experience and type safety. It's a ground-up
7//! implementation with hand-rolled types based on actual NEAR RPC responses.
8//!
9//! ## Quick Start
10//!
11//! Add `near-kit` to your `Cargo.toml`:
12//!
13//! ```bash
14//! cargo add near-kit
15//! ```
16//!
17//! ### Read-Only Operations
18//!
19//! No credentials needed for querying blockchain state:
20//!
21//! ```rust,no_run
22//! use near_kit::*;
23//!
24//! #[tokio::main]
25//! async fn main() -> Result<(), Error> {
26//! let near = Near::testnet().build();
27//!
28//! // Check account balance
29//! let balance = near.balance("alice.testnet").await?;
30//! println!("Balance: {}", balance.available);
31//!
32//! // Call a view function
33//! let count: u64 = near.view("counter.testnet", "get_count").await?;
34//! println!("Count: {}", count);
35//!
36//! Ok(())
37//! }
38//! ```
39//!
40//! ### Transactions (Writes)
41//!
42//! For state-changing operations, configure a signer:
43//!
44//! ```rust,no_run
45//! use near_kit::*;
46//!
47//! #[tokio::main]
48//! async fn main() -> Result<(), Error> {
49//! let near = Near::testnet()
50//! .credentials("ed25519:YOUR_PRIVATE_KEY", "your-account.testnet")?
51//! .build();
52//!
53//! // Transfer NEAR
54//! near.transfer("bob.testnet", NearToken::near(1)).await?;
55//!
56//! // Call a contract function
57//! near.call("counter.testnet", "increment")
58//! .gas("30 Tgas")
59//! .await?;
60//!
61//! Ok(())
62//! }
63//! ```
64//!
65//! ## Design Principles
66//!
67//! 1. **Single entry point** — Everything flows through the [`Near`] client
68//! 2. **Configure once** — Network and signer are set at client creation
69//! 3. **Type-safe but ergonomic** — Accept both typed values and string parsing
70//! 4. **Explicit units** — No ambiguous amounts; must specify `NEAR`, `yocto`, `Tgas`, etc.
71//! 5. **Progressive disclosure** — Simple things are simple; advanced options available when needed
72//!
73//! ## Core Types
74//!
75//! | Type | Description |
76//! |------|-------------|
77//! | [`Near`] | Main client — the single entry point for all operations |
78//! | [`AccountId`] | Validated NEAR account identifier |
79//! | [`NearToken`] | Token amount with yoctoNEAR precision |
80//! | [`Gas`] | Gas units for transactions |
81//! | [`PublicKey`] / [`SecretKey`] | Ed25519 cryptographic keys |
82//! | [`CryptoHash`] | 32-byte SHA-256 hash (blocks, transactions) |
83//!
84//! ## Working with Amounts
85//!
86//! ### Typed Constructors (Recommended)
87//!
88//! For compile-time safety, use the typed constructors:
89//!
90//! ```rust
91//! use near_kit::{NearToken, Gas};
92//!
93//! // NEAR amounts
94//! let five_near = NearToken::near(5);
95//! let half_near = NearToken::millinear(500);
96//! let one_yocto = NearToken::yocto(1);
97//!
98//! // Gas amounts
99//! let gas = Gas::tgas(30); // 30 teragas
100//! let more_gas = Gas::ggas(5); // 5 gigagas
101//! ```
102//!
103//! ### String Parsing (Runtime Input)
104//!
105//! For CLI arguments, config files, or user input:
106//!
107//! ```rust
108//! use near_kit::{NearToken, Gas};
109//!
110//! // Parse NEAR amounts
111//! let amount: NearToken = "5 NEAR".parse().unwrap();
112//! let small: NearToken = "100 milliNEAR".parse().unwrap();
113//! let tiny: NearToken = "1000 yocto".parse().unwrap();
114//!
115//! // Parse gas
116//! let gas: Gas = "30 Tgas".parse().unwrap();
117//! ```
118//!
119//! Many builder methods also accept strings directly:
120//!
121//! ```rust,no_run
122//! # use near_kit::*;
123//! # async fn example(near: &Near) -> Result<(), Error> {
124//! near.call("contract.testnet", "method")
125//! .gas("100 Tgas")
126//! .deposit("0.1 NEAR")
127//! .await?;
128//! # Ok(())
129//! # }
130//! ```
131//!
132//! ## Query Builders
133//!
134//! Read operations return query builders that can be customized before awaiting:
135//!
136//! ```rust,no_run
137//! use near_kit::*;
138//!
139//! # async fn example() -> Result<(), Error> {
140//! let near = Near::testnet().build();
141//!
142//! // Simple query
143//! let balance = near.balance("alice.testnet").await?;
144//!
145//! // Query at a specific block height
146//! let old_balance = near.balance("alice.testnet")
147//! .at_block(100_000_000)
148//! .await?;
149//!
150//! // Query with different finality
151//! let optimistic = near.balance("alice.testnet")
152//! .finality(Finality::Optimistic)
153//! .await?;
154//! # Ok(())
155//! # }
156//! ```
157//!
158//! ## Transaction Builders
159//!
160//! Write operations return transaction builders for customization:
161//!
162//! ```rust,no_run
163//! use near_kit::*;
164//!
165//! # async fn example(near: &Near) -> Result<(), Error> {
166//! // Function call with arguments, gas, and deposit
167//! near.call("nft.testnet", "nft_mint")
168//! .args(serde_json::json!({ "token_id": "1", "receiver_id": "alice.testnet" }))
169//! .gas("100 Tgas")
170//! .deposit("0.1 NEAR")
171//! .await?;
172//!
173//! // Wait for different execution levels
174//! near.transfer("bob.testnet", NearToken::near(1))
175//! .wait_until(TxExecutionStatus::Final)
176//! .await?;
177//! # Ok(())
178//! # }
179//! ```
180//!
181//! ## Multi-Action Transactions
182//!
183//! Chain multiple actions into a single atomic transaction:
184//!
185//! ```rust,no_run
186//! use near_kit::*;
187//!
188//! # async fn example(near: &Near) -> Result<(), Box<dyn std::error::Error>> {
189//! // Create a sub-account with funding and deploy a contract
190//! let new_key: PublicKey = "ed25519:6E8sCci...".parse()?;
191//! let wasm = std::fs::read("contract.wasm")?;
192//!
193//! near.transaction("sub.alice.testnet")
194//! .create_account()
195//! .transfer(NearToken::near(10))
196//! .add_full_access_key(new_key)
197//! .deploy(wasm)
198//! .call("new")
199//! .args(serde_json::json!({ "owner_id": "alice.testnet" }))
200//! .send()
201//! .await?;
202//! # Ok(())
203//! # }
204//! ```
205//!
206//! ## Token Helpers
207//!
208//! Built-in support for fungible (NEP-141) and non-fungible (NEP-171) tokens.
209//!
210//! For common tokens like USDC, USDT, and wNEAR, use the provided [`tokens`] constants
211//! to avoid copy-pasting addresses. They automatically resolve to the correct address
212//! based on the network:
213//!
214//! ```rust,no_run
215//! use near_kit::*;
216//!
217//! # async fn example() -> Result<(), Error> {
218//! let near = Near::mainnet().build();
219//!
220//! // Known tokens auto-resolve based on network
221//! let usdc = near.ft(tokens::USDC)?;
222//! let balance = usdc.balance_of("alice.near").await?;
223//! println!("Balance: {}", balance); // "1.50 USDC"
224//!
225//! // Or use raw addresses for any token
226//! let custom = near.ft("my-token.near")?;
227//!
228//! // NFTs (NEP-171)
229//! let nft = near.nft("nft.testnet")?;
230//! let tokens = nft.tokens_for_owner("alice.testnet", None, Some(10)).await?;
231//! # Ok(())
232//! # }
233//! ```
234//!
235//! Available known tokens: [`tokens::USDC`], [`tokens::USDT`], [`tokens::W_NEAR`]
236//!
237//! ## Typed Contract Interfaces
238//!
239//! Use the `#[near_kit::contract]` macro for compile-time type safety:
240//!
241//! ```rust,ignore
242//! use near_kit::*;
243//! use serde::Serialize;
244//!
245//! #[near_kit::contract]
246//! pub trait Counter {
247//! // View method (read-only, no signer needed)
248//! fn get_count(&self) -> u64;
249//!
250//! // Change method (requires signer)
251//! #[call]
252//! fn increment(&mut self);
253//!
254//! // Change method with arguments
255//! #[call]
256//! fn add(&mut self, args: AddArgs);
257//! }
258//!
259//! #[derive(Serialize)]
260//! pub struct AddArgs {
261//! pub value: u64,
262//! }
263//!
264//! async fn example(near: &Near) -> Result<(), Error> {
265//! let counter = near.contract::<Counter>("counter.testnet");
266//!
267//! // Type-safe view call
268//! let count = counter.get_count().await?;
269//!
270//! // Type-safe change calls
271//! counter.increment().await?;
272//! counter.add(AddArgs { value: 5 }).await?;
273//!
274//! Ok(())
275//! }
276//! ```
277//!
278//! ## Signers
279//!
280//! Several signer implementations are available:
281//!
282//! | Signer | Use Case |
283//! |--------|----------|
284//! | [`InMemorySigner`] | Simple scripts with a private key |
285//! | [`FileSigner`] | Load from `~/.near-credentials` (near-cli compatible) |
286//! | [`EnvSigner`] | CI/CD environments via `NEAR_ACCOUNT_ID` / `NEAR_PRIVATE_KEY` |
287//! | [`RotatingSigner`] | High-throughput with multiple keys (avoids nonce collisions) |
288//! | [`KeyringSigner`] | System keyring (macOS Keychain, etc.) — requires `keyring` feature |
289//!
290//! ```rust,no_run
291//! use near_kit::*;
292//!
293//! # fn example() -> Result<(), Error> {
294//! // Using credentials directly
295//! let near = Near::testnet()
296//! .credentials("ed25519:...", "alice.testnet")?
297//! .build();
298//!
299//! // Using a custom signer
300//! let signer = FileSigner::new("testnet", "alice.testnet")?;
301//! let near = Near::testnet().signer(signer).build();
302//!
303//! // Environment variables (CI/CD)
304//! let signer = EnvSigner::new()?;
305//! let near = Near::testnet().signer(signer).build();
306//! # Ok(())
307//! # }
308//! ```
309//!
310//! ### Multiple Accounts
311//!
312//! For production apps that manage multiple accounts, use [`Near::with_signer`] to
313//! derive clients that share the same RPC connection but sign as different accounts:
314//!
315//! ```rust,no_run
316//! use near_kit::*;
317//!
318//! # fn example() -> Result<(), Error> {
319//! let near = Near::testnet().build();
320//!
321//! let alice = near.with_signer(InMemorySigner::new("alice.testnet", "ed25519:...")?);
322//! let bob = near.with_signer(InMemorySigner::new("bob.testnet", "ed25519:...")?);
323//!
324//! // Both share the same connection, no overhead
325//! // alice.transfer("carol.testnet", NearToken::near(1)).await?;
326//! // bob.transfer("carol.testnet", NearToken::near(2)).await?;
327//! # Ok(())
328//! # }
329//! ```
330//!
331//! Use [`with_signer`](Near::with_signer) for multi-account management and
332//! [`RotatingSigner`] for high-throughput single-account usage (multiple keys to
333//! avoid nonce collisions). For one-off overrides on a single transaction, use
334//! [`.sign_with()`](TransactionBuilder::sign_with) on the transaction builder.
335//!
336//! ## Sandbox Testing
337//!
338//! Enable the `sandbox` feature for local testing with [`near-sandbox`](https://crates.io/crates/near-sandbox):
339//!
340//! ```toml
341//! [dev-dependencies]
342//! near-kit = { version = "0.1", features = ["sandbox"] }
343//! near-sandbox = "0.3"
344//! ```
345//!
346//! ```rust,ignore
347//! use near_kit::*;
348//! use near_sandbox::Sandbox;
349//!
350//! #[tokio::test]
351//! async fn test_contract() {
352//! let sandbox = Sandbox::start().await.unwrap();
353//! let near = Near::sandbox(&sandbox);
354//!
355//! // Root account is pre-configured with credentials
356//! near.transfer("alice.sandbox", NearToken::near(10)).await.unwrap();
357//! }
358//! ```
359//!
360//! ## Feature Flags
361//!
362//! | Feature | Description |
363//! |---------|-------------|
364//! | `sandbox` | Integration with `near-sandbox` for local testing |
365//! | `keyring` | System keyring signer (macOS Keychain, Windows Credential Manager, etc.) |
366//!
367//! ## Error Handling
368//!
369//! All operations return `Result<T, near_kit::Error>`. The [`Error`] type provides
370//! detailed information about failures:
371//!
372//! ```rust,no_run
373//! use near_kit::*;
374//!
375//! # async fn example() -> Result<(), Error> {
376//! let near = Near::testnet().build();
377//!
378//! match near.balance("nonexistent.testnet").await {
379//! Ok(balance) => println!("Balance: {}", balance.available),
380//! Err(Error::Rpc(RpcError::AccountNotFound(account))) => {
381//! println!("Account {} doesn't exist", account);
382//! }
383//! Err(e) => return Err(e),
384//! }
385//! # Ok(())
386//! # }
387//! ```
388
389pub mod client;
390pub mod contract;
391pub mod error;
392pub mod tokens;
393pub mod types;
394
395// Sandbox module - only available with "sandbox" feature
396#[cfg(feature = "sandbox")]
397pub mod sandbox;
398
399// Re-export commonly used types at crate root
400pub use error::{Error, RpcError};
401pub use types::nep413;
402pub use types::*;
403
404// Re-export contract types
405pub use contract::{Contract, ContractClient};
406
407// Re-export client types
408pub use client::{
409 AccessKeysQuery, AccountExistsQuery, AccountQuery, BalanceQuery, CallBuilder, DelegateOptions,
410 DelegateResult, EnvSigner, FileSigner, InMemorySigner, Near, NearBuilder, RetryConfig,
411 RotatingSigner, RpcClient, SandboxNetwork, Signer, SigningKey, TransactionBuilder,
412 TransactionSend, ViewCall, ViewCallBorsh,
413};
414
415#[cfg(feature = "keyring")]
416pub use client::KeyringSigner;
417
418// Re-export token types
419pub use tokens::{
420 FtAmount, FtMetadata, FungibleToken, IntoContractId, KnownToken, NftContractMetadata, NftToken,
421 NftTokenMetadata, NonFungibleToken, StorageBalance, StorageBalanceBounds, StorageDepositCall,
422 USDC, USDT, W_NEAR,
423};
424
425// Re-export proc macros
426pub use near_kit_macros::borsh;
427pub use near_kit_macros::call;
428pub use near_kit_macros::contract;
429pub use near_kit_macros::json;