Metadata-Version: 2.5
Name: near-kit
Version: 1.3.0
Summary: A Pythonic SDK for NEAR Protocol. Feels like requests.
Project-URL: Homepage, https://github.com/r-near/near-kit-py
Project-URL: Repository, https://github.com/r-near/near-kit-py
Author: r-near
License-Expression: MIT
License-File: LICENSE
Keywords: blockchain,borsh,crypto,near,sdk,web3
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: base58>=2.1
Requires-Dist: cryptography>=48
Requires-Dist: httpx>=0.27
Requires-Dist: mnemonic>=0.21
Requires-Dist: pyborsh>=1.0
Requires-Dist: pycryptodome>=3.20
Requires-Dist: pydantic>=2.0
Provides-Extra: dev
Requires-Dist: mypy>=1.18; extra == 'dev'
Requires-Dist: pre-commit>=4.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.26; extra == 'dev'
Requires-Dist: pytest-cov>=6.0; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.14; extra == 'dev'
Description-Content-Type: text/markdown

# near-kit

[![CI](https://github.com/r-near/near-kit-py/actions/workflows/ci.yml/badge.svg)](https://github.com/r-near/near-kit-py/actions/workflows/ci.yml)
[![codecov](https://codecov.io/gh/r-near/near-kit-py/graph/badge.svg?token=pnZFDwQ326)](https://codecov.io/gh/r-near/near-kit-py)
[![PyPI version](https://img.shields.io/pypi/v/near-kit.svg)](https://pypi.org/project/near-kit/)
[![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

**A Pythonic SDK for NEAR Protocol. Feels like `requests`.**

```python
import near

near.view("counter.near", "get_count")          # one line, no setup
near.balance("alice.near")                       # -> Amount('42.5 NEAR')
```

Human-readable everywhere — `"10 NEAR"` and `"30 Tgas"`, never
`10000000000000000000000000`. Typed errors, exact integer math, sync *and*
async, post-quantum keys, and the full nearcore 2.13 action set (global
contracts, gas keys, deterministic accounts). The Python sibling of
[near-kit](https://github.com/r-near/near-kit) for TypeScript.

## Install

```bash
uv add near-kit        # or: pip install near-kit
```

Python 3.11+.

## The rule of the API

> **Reads are stateless; if it signs, you need a client.**

Module functions cover one-off reads, `requests.get()`-style. A `Near`
client holds the stateful things: your signer, a nonce cache, and a
connection pool.

```python
from near import Near

# Explicit config...
client = Near(network="testnet",
              account_id="alice.testnet",
              private_key="ed25519:...")

# ...or zero-argument discovery: NEAR_NETWORK / NEAR_ACCOUNT_ID /
# NEAR_PRIVATE_KEY env vars, falling back to ~/.near-credentials.
client = Near()

# ...or straight from NEAR CLI credentials:
client = Near.from_file("alice.testnet", network="testnet")
```

### Reads

```python
client.view("counter.testnet", "get_count")
client.balance()                      # your balance -> Amount
client.balance("bob.testnet")         # anyone's
client.account("bob.testnet")         # AccountView (balance, storage, code hash)
client.account_exists("bob.testnet")
client.access_keys("bob.testnet")
client.transaction_status(tx_hash)
client.rpc("block", {"finality": "final"})   # escape hatch: any RPC method
```

### Writes

```python
client.send("bob.testnet", "5 NEAR")
client.call("counter.testnet", "increment", {"by": 2},
            deposit="0.1 NEAR", gas="30 Tgas")
```

Multi-action transactions are just data — a list of actions, one receiver,
executed atomically:

```python
from near import create_account, transfer, deploy_contract, function_call, add_full_access_key

client.send_transaction(
    "sub.alice.testnet",
    actions=[
        create_account(),
        transfer("5 NEAR"),
        add_full_access_key(key.public_key),
        deploy_contract(wasm_bytes),
        function_call("init", {"owner": "alice.testnet"}),
    ],
)
```

### Tokens

NEP-141 fungible tokens are first-class: amounts are human strings in the
token's own decimals, and `register=True` batches the receiver's
`storage_deposit` into the same atomic transaction when they aren't
registered yet.

```python
client.ft_transfer("usdt.tether-token.near", "bob.near", "5.25 USDT", register=True)

balance = client.ft_balance("usdt.tether-token.near")   # TokenAmount, exact int
print(balance)                                          # 5.25 USDT
balance.display                                         # Decimal('5.25')
```

NFTs (NEP-171) get the same treatment: `nft_transfer` attaches the required
1 yoctoNEAR, `nft_token` / `nft_tokens_for_owner` cover lookups.

### Async

`AsyncNear` is the same surface, awaited — for FastAPI services, relayers,
and MCP servers:

```python
from near import AsyncNear

async with AsyncNear() as client:
    await client.call("counter.testnet", "increment")
    results = await asyncio.gather(*(client.send(acc, "0.1 NEAR") for acc in accounts))
```

Concurrent transactions from one account just work — the client manages
nonces and retries collisions automatically.

## Amounts are exact

`Amount` is an `int` subclass denominated in yoctoNEAR, so arithmetic and
comparisons are exact; it parses and prints human strings. Bare numbers are
rejected at API boundaries — unit confusion is a bug that should not compile.

```python
from near import Amount

bal = client.balance()          # Amount(5250000000000000000000000)
str(bal)                        # '5.25 NEAR'
bal.as_near                     # Decimal('5.25')
bal > Amount("1 NEAR")          # True
client.send("bob.near", 5)      # UnitParseError: write "5 NEAR" or "5 yocto"
```

## Typed errors

```python
from near import ContractPanicError, AccountNotFoundError

try:
    client.call("app.near", "buy", deposit="1 NEAR")
except ContractPanicError as e:
    print(e.panic)     # the contract's panic message
    print(e.logs)      # contract logs leading up to it
```

Every error derives from `NearError` with a stable `.code` and a
`.retryable` flag.

## Testing your app: the sandbox is a database

Run NEAR like you'd run postgres in CI:

```bash
docker run -d -p 3030:3030 nearprotocol/sandbox:2.13.4
```

The sandbox root account's key is deterministic (derived from the image's
test seed), so nothing needs to be copied out of the container:

```python
from near import Near
from near.testing import sandbox_signer

near = Near(rpc_url="http://localhost:3030", signer=sandbox_signer())
near.send("anything.sandbox", ...)   # root account, fully funded
```

GitHub Actions:

```yaml
services:
  near-sandbox:
    image: nearprotocol/sandbox:2.13.4
    ports: ["3030:3030"]
```

Or let pytest do all of it — installing near-kit registers a plugin. Its
`near_sandbox` fixture reuses `NEAR_SANDBOX_URL` or localhost:3030 when
reachable, otherwise starts (and later removes) a Docker sandbox on a free
port; `sandbox_near` is a `Near` client signing as the root account:

```python
from near import add_full_access_key, create_account, generate_key, transfer
from near.testing import fast_forward


def test_my_app(sandbox_near):
    key = generate_key()
    sandbox_near.send_transaction(  # root creates + funds a fresh account
        "alice.sandbox",
        actions=[create_account(), transfer("10 NEAR"), add_full_access_key(key.public_key)],
    )
    fast_forward(sandbox_near, 100)  # time travel: 100 blocks, no waiting
```

## Meta-transactions (NEP-366)

User signs, relayer pays gas:

```python
# User side — no gas spent:
signed = client.sign_delegate("app.near", actions=[function_call("claim", {})])
payload = encode_signed_delegate(signed)          # base64, POST it to your relayer

# Relayer side:
relayer.send_delegate(payload)
```

nearcore 2.13's `DelegateV2` (NEP-611, the gas-key flavour of delegate
actions) is deliberately not modeled while the feature is reworked upstream.

## Global contracts

Publish WASM once; any account then deploys it by reference instead of
paying to store its own copy:

```python
from near import publish_contract, use_global_contract

# Publisher: identified by your account (re-publish to update every user)
# or by code hash (immutable).
client.send_transaction(me, actions=[publish_contract(wasm_bytes)])
client.send_transaction(me, actions=[publish_contract(wasm_bytes, identified_by="hash")])

# Anyone: run the published code on their own account.
client.send_transaction("app.alice.near",
                        actions=[use_global_contract(account_id="publisher.near")])

client.global_contract(account_id="publisher.near")   # ContractCode(code=..., hash=...)
client.global_contract_exists(code_hash="5FzD...")
client.contract_code("app.alice.near")                # the code an account runs
```

## Gas keys

A gas key is an access key with its own prepaid balance for gas and several
independent nonce lanes, so one key can keep many transactions in flight
(nearcore 2.13):

```python
from near import add_gas_key, transfer_to_gas_key, withdraw_from_gas_key

client.send_transaction(me, actions=[add_gas_key(gas_pk, num_nonces=8),
                                     transfer_to_gas_key(gas_pk, "5 NEAR")])

# Sign with the gas key and pick a lane; gas comes out of the key's balance,
# deposits still come out of the account.
as_gas_key = client.with_signer(KeyPairSigner(me, gas_key))
as_gas_key.send_transaction("app.near", [function_call("ping")], gas_key_index=3)

client.gas_key_nonces(me, gas_pk)                 # one nonce per lane
client.access_key(me, gas_pk).gas_key_balance     # what is left for gas
client.send_transaction(me, actions=[withdraw_from_gas_key(gas_pk, "1 NEAR")])
```

Any key can also ask for `strict_nonce=True`, which makes the node accept
only "current nonce + 1" — for pipelines that must stay strictly sequential.
Concurrent strict sends on one key (or lane) take turns inside the client,
so all of them land; ordinary sends keep overlapping freely.

## Deterministic accounts (NEP-616)

An account whose ID is a function of its initial state — a global contract
plus seed storage — so the address is known before it exists and anyone can
bring it to life:

```python
from near import deterministic_state_init, derive_deterministic_account_id

action = deterministic_state_init(account_id="publisher.near",
                                  data={b"owner": b"alice.near"}, deposit="1 NEAR")
account_id = derive_deterministic_account_id(action.state_init)   # "0s" + 40 hex chars
client.send_transaction(account_id, actions=[action])
```

The seed map is encoded canonically (keys sorted bytewise, as nearcore's
`BTreeMap`) and the node re-derives the ID, so a mismatch is rejected instead
of creating a stray account. nearcore master's `UniversalStateInit`
(unreleased) is not modeled yet.

## Message signing (NEP-413)

Off-chain auth ("login with NEAR"):

```python
signed = client.sign_message("Login to MyApp", recipient="myapp.com")

# Server side:
from near import verify_message
verify_message(signed, "Login to MyApp", "myapp.com", nonce)
```

## Post-quantum keys

ML-DSA-65 (FIPS 204) keys work everywhere ed25519 keys do:

```python
from near import MlDsa65KeyPair

pq = MlDsa65KeyPair.generate()
client.send_transaction(account_id, actions=[add_full_access_key(pq.public_key)])
```

## Custom signers (KMS, HSM)

Anything with `account_id`, `public_key`, and `sign(message) -> bytes` is a
signer:

```python
class KmsSigner:
    account_id = "treasury.near"
    public_key = PublicKey.parse("ed25519:...")

    def sign(self, message: bytes) -> bytes:
        return kms.sign(key_id=..., message=message)

client = Near(network="mainnet", signer=KmsSigner())
```

## What's under the hood

Borsh serialization via [pyborsh](https://github.com/r-near/pyborsh)
(Pydantic-native, byte-verified against Rust), ed25519/ML-DSA via
[pyca/cryptography](https://cryptography.io), NEP-616 Keccak-256 via
[pycryptodome](https://www.pycryptodome.org), HTTP via
[httpx](https://www.python-httpx.org). Every transaction byte this library
produces is verified end-to-end against a real nearcore node in CI.

## License

MIT
