Metadata-Version: 2.4
Name: token-network
Version: 0.2.2
Summary: Validate input and return token network config (e.g. network.bitcoin, network.bsc.usdt)
License: MIT
Keywords: blockchain,tokens,networks,crypto,config
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.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.8
Description-Content-Type: text/markdown
Requires-Dist: PyYAML>=6.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Dynamic: requires-python

# token-network

Python library that validates input and returns token network config. Resolve networks and tokens by name/symbol and get merged config (e.g. USDT on BSC with contract address and decimals).

## Install

```bash
pip install token-network
```

From source (development):

```bash
pip install -e .
```

## Quick start

**Mainnet** (default) — uses `token_networks.yaml`:

```python
from token_network import network, get_token_on_network, get_network, get_token, get_token_network, TokenNetworkError

# Network by name (callable)
bitcoin = network("bitcoin")
bitcoin.decimal              # 8 (base token decimals)
bitcoin.confirmation_number  # 2
bitcoin.__dict__()           # all network data: {"config": {...}, "tokens": [...]}

# Attribute access works too
network.bitcoin.config       # Bitcoin network config
network.ethereum.tokens      # list of TokenOnNetwork objects

# Token on a network (object with .contract_address, .__dict__(), etc.)
token_on_network = get_token_on_network(network="ethereum", token_abbr="USDT")
token_on_network.contract_address   # "0xdAC17F958D2ee523a2206206994597C13D831ec7"
token_on_network.__dict__()         # all token-on-network data

# By name/symbol (dicts)
get_network("bitcoin")
get_token("USDT")
get_token_network("USDT", "bsc")
```

**Testnet** — use a dedicated accessor with `token_networks_testnet.yaml`:

```python
from token_network import NetworkAccessor, TokenNetworkError

testnet = NetworkAccessor(testnet=True)
testnet("bsc").usdt          # USDT on BSC from testnet config (TokenOnNetwork)
testnet.get_network("bsc")
testnet.get_token_network("USDT", "bsc")
```

---

## API reference

All identifiers (network name, token symbol/slug/name) are **case-insensitive**. Unknown network or token raises `TokenNetworkError`.

### Network: `network(name)` and `network.<name>`

The default `network` object uses **mainnet** config. You can get a network **by name** (callable) or by **attribute**:

- `bitcoin = network("bitcoin")` — same as `network.bitcoin`
- `bitcoin.decimal` — base token decimals (e.g. 8 for Bitcoin, 18 for Ethereum)
- `bitcoin.confirmation_number` — required confirmations (e.g. 2 for Bitcoin)
- `bitcoin.config` — full network config dict
- `bitcoin.tokens` — **list of `TokenOnNetwork` objects** (see below)
- `bitcoin.__dict__()` — all network data: `{"config": {...}, "tokens": [...]}`
- `bitcoin.to_dict()` — same as above (legacy: `tokens` as raw dicts)
- `bitcoin.usdt` — `TokenOnNetwork` for USDT on this network (if defined)

For **testnet**, use `NetworkAccessor(testnet=True)` and call it the same way.

| Usage | Returns |
|--------|--------|
| `network("bitcoin")` / `network.bitcoin` | Network node (see above) |
| `network.bitcoin.decimal` | `int` — base token decimals |
| `network.bitcoin.confirmation_number` | `int` — confirmation count |
| `network.bitcoin.config` | Network config dict |
| `network.bitcoin.tokens` | `list[TokenOnNetwork]` — token objects on this network |
| `network.bitcoin.__dict__()` | `{"config": {...}, "tokens": [...]}` |
| `network.bsc.usdt` | `TokenOnNetwork` (contract_address, decimal, .__dict__(), etc.) |

**Example**

```python
bitcoin = network("bitcoin")
bitcoin.decimal              # 8
bitcoin.confirmation_number  # 2
bitcoin.__dict__()           # {"config": {...}, "tokens": [...]}

ethereum = network("ethereum")
ethereum.tokens              # [TokenOnNetwork(ETH), TokenOnNetwork(USDT), ...]
ethereum.usdt.contract_address  # "0xdAC17F958D2ee523a2206206994597C13D831ec7"
```

### TokenOnNetwork (tokens on a network)

`network.<net>.<token>` (e.g. `network.ethereum.usdt`) and `get_token_on_network(...)` return a **`TokenOnNetwork`** object with:

| Attribute / method | Description |
|--------------------|-------------|
| `.contract_address` | Contract address or `None` for native coin |
| `.decimal` / `.decimals` | Decimals on this network |
| `.native` | `True` if native coin |
| `.network` | Network config dict |
| `.token` | Token info dict (symbol, slug, name, …) |
| `.type` | `"COIN"` or `"TOKEN"` |
| `.__dict__()` | All token-on-network data as a dict |
| `["key"]` | Dict-like access (e.g. `["contract_address"]`, `["token_info"]`) |

**Example**

```python
from token_network import get_token_on_network

ton = get_token_on_network(network="ethereum", token_abbr="USDT")
ton.contract_address   # "0xdAC17F958D2ee523a2206206994597C13D831ec7"
ton.decimal            # 6
ton.__dict__()         # {"network": {...}, "token": {...}, "contract_address": "...", ...}
ton["contract_address"]  # same as ton.contract_address
```

---

### `get_networks()`

Returns a sorted list of all network ids.

**Returns:** `list[str]` — e.g. `['bitcoin', 'bsc', 'dogecoin', 'ethereum', 'ripple', 'solana', 'tron']`

**Example**

```python
from token_network import get_networks
get_networks()
# ['bitcoin', 'bsc', 'dogecoin', 'ethereum', 'ripple', 'solana', 'tron']
```

Also available as `network.get_networks()`.

---

### `get_tokens()`

Returns a sorted list of all token symbols.

**Returns:** `list[str]` — e.g. `['AAVE', 'BNB', 'BTC', 'ETH', 'USDT', ...]`

**Example**

```python
from token_network import get_tokens
get_tokens()
# ['AAVE', 'BNB', 'BTC', 'DOGE', 'ETH', 'LINK', 'SHIB', 'SOL', 'TRX', 'UNI', 'USDC', 'USDT', 'XRP']
```

Also available as `network.get_tokens()`.

---

### `get_token(identifier)`

Get token object by **symbol**, **slug**, or **name** (case-insensitive).

**Parameters**

- `identifier` — Token symbol (e.g. `USDT`), slug (e.g. `usdt`), or name (e.g. `tether`).

**Returns:** `dict` — Token info: `slug`, `symbol`, `standard_symbol`, `name`, `precision`, `factor`.

**Raises:** `TokenNetworkError` if no token matches.

**Example**

```python
from token_network import get_token
get_token("USDT")
# {'slug': 'usdt', 'symbol': 'USDT', 'standard_symbol': 'USDT', 'name': 'tether', 'precision': 6, 'factor': '1e6'}
get_token("tether")   # same
get_token("btc")      # Bitcoin token info
```

Also available as `network.get_token(identifier)`.

---

### `get_network(identifier)`

Get network object by **network name/id** (case-insensitive).

**Parameters**

- `identifier` — Network name (e.g. `bitcoin`, `bsc`, `ethereum`).

**Returns:** `dict` with keys:
- `config` — Network config (network_type, token_standard, base_token, confirmation_number, etc.).
- `tokens` — List of token bindings on this network.

**Raises:** `TokenNetworkError` if network is unknown.

**Example**

```python
from token_network import get_network
get_network("bitcoin")
# {'config': {'network_type': 'UTXO', 'base_token': 'BTC', ...}, 'tokens': [...]}
get_network("BSC")
```

Also available as `network.get_network(identifier)`. Same shape as `network.bitcoin.to_dict()`.

---

### `get_token_network(token_identifier, network_identifier)`

Get token_network config for a **token** on a **network** (case-insensitive).

**Parameters**

- `token_identifier` — Token symbol, slug, or name (e.g. `USDT`, `usdt`, `tether`).
- `network_identifier` — Network name (e.g. `bsc`, `ethereum`).

**Returns:** `dict` with keys:
- `network` — Network config.
- `token` — Token info (slug, symbol, name, precision, factor).
- `contract_address` — Contract address or `None` for native.
- `decimal` / `decimals` — Decimals on this network.
- `native` / `type` — Whether it is the chain’s native asset.

**Raises:** `TokenNetworkError` if token or network is unknown, or if the token is not on that network.

**Example**

```python
from token_network import get_token_network
get_token_network("USDT", "bsc")
# {'network': {...}, 'token': {...}, 'contract_address': '0x55d398326f99059fF775485246999027B3197955', 'decimal': 18, 'native': False}
get_token_network("tether", "BSC")   # same
get_token_network("ETH", "ethereum")
```

Same data as `network.bsc.usdt` (as a dict). Also available as `network.get_token_network(token_identifier, network_identifier)`.

---

### `get_token_on_network(*, network=..., token_abbr=...)`

Get a **token-on-network object** by network name and token symbol/slug (case-insensitive).

**Parameters**

- `network` — Network name (e.g. `"ethereum"`, `"bsc"`).
- `token_abbr` — Token symbol or slug (e.g. `"USDT"`, `"usdt"`).

**Returns:** `TokenOnNetwork` — Object with `.contract_address`, `.decimal`, `.token`, `.network`, `.__dict__()`, and dict-like access.

**Raises:** `TokenNetworkError` if network or token is unknown, or token is not on that network.

**Example**

```python
from token_network import get_token_on_network

ton = get_token_on_network(network="ethereum", token_abbr="USDT")
ton.contract_address   # "0xdAC17F958D2ee523a2206206994597C13D831ec7"
ton.__dict__()          # all token-on-network data
```

Also available as `network.get_token_on_network(network=..., token_abbr=...)`. Same data as `network.ethereum.usdt` (which returns a `TokenOnNetwork`).

---

### `TokenNetworkError`

Exception raised when:
- A network name is unknown.
- A token (symbol/slug/name) is unknown.
- A token is requested on a network where it is not defined.

**Example**

```python
from token_network import get_network, get_token, get_token_network, TokenNetworkError

try:
    get_network("unknown_chain")
except TokenNetworkError as e:
    print(e)  # Unknown network: 'unknown_chain'. Known networks: bitcoin, bsc, ...

try:
    get_token("unknown_token")
except TokenNetworkError as e:
    print(e)  # Unknown token: 'unknown_token'. Known tokens: [...]

try:
    get_token_network("SHIB", "bitcoin")
except TokenNetworkError as e:
    print(e)  # Token 'SHIB' is not on network 'bitcoin'. Available on this network: ['BTC']
```

---

## Data sources

Config is loaded from YAML in the package’s `data` directory:

| File | Content |
|------|--------|
| `networks.yaml` | Chain config (network_type, token_standard, base_token, confirmation_number, …) |
| `tokens.yaml` | Token definitions (symbol, slug, name, precision, factor) |
| `token_networks.yaml` | Token–network bindings for **mainnet** (contract_address, decimal, native) |
| `token_networks_testnet.yaml` | Token–network bindings for **testnet** (used when `NetworkAccessor(testnet=True)`) |

- The default `network` and top-level helpers (`get_network`, `get_token_network`, …) use **mainnet** (`token_networks.yaml`).
- For testnet, use `NetworkAccessor(testnet=True)`; it loads `token_networks_testnet.yaml` instead.

To change data, edit the YAML files in `token_network/data/`.

---

## Publishing to PyPI

To allow `pip install token-network` from PyPI:

1. Create an account on [pypi.org](https://pypi.org).
2. `pip install build twine`
3. `python -m build` then `twine upload dist/*`

For testing: `twine upload --repository testpypi dist/*`, then  
`pip install --index-url https://test.pypi.org/simple/ token-network`.

---

## Development

```bash
pip install -e ".[dev]"
pytest
```
