Metadata-Version: 2.3
Name: intentlayer-sdk
Version: 0.3.0
Summary: Python SDK for IntentLayer - handles intent recording and IPFS pinning
License: MIT License
         
         Copyright (c) 2025 IntentLayer contributors
         
         Permission is hereby granted, free of charge, to any person obtaining a copy
         of this software and associated documentation files (the "Software"), to deal
         in the Software without restriction, including without limitation the rights
         to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
         copies of the Software, and to permit persons to whom the Software is
         furnished to do so, subject to the following conditions:
         
         The above copyright notice and this permission notice shall be included in all
         copies or substantial portions of the Software.
         
         THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
         IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
         FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
         AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
         LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
         OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
         SOFTWARE.
Author: Ryan Sevey
Author-email: dev@intentlayer.net
Requires-Python: >=3.11,<4.0
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Requires-Dist: base58 (>=2.1.1)
Requires-Dist: cryptography (>=44.0.0)
Requires-Dist: pydantic (>=2.5.0)
Requires-Dist: requests (>=2.31.0)
Requires-Dist: typer (>=0.9.0)
Requires-Dist: web3 (>=7.10.0,<8.0.0)
Description-Content-Type: text/markdown

[![PyPI version](https://img.shields.io/pypi/v/intentlayer-sdk.svg)](https://pypi.org/project/intentlayer-sdk/)  
[![Test Coverage](https://img.shields.io/codecov/c/github/IntentLayer/intentlayer-python-sdk.svg?branch=main)](https://app.codecov.io/gh/IntentLayer/intentlayer-python-sdk)  
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)

# IntentLayer SDK for Python

A batteries-included client for the IntentLayer protocol: pin JSON payloads to IPFS, generate cryptographically-signed envelopes, and record intents on any EVM-compatible chain in a single call.

---

## 🚀 Key Benefits

- **Verifiable Audit Trail**  
  Tie every action to a Decentralized Identifier (DID) and immutably log a hash on-chain.

- **Built-in Incentives**  
  Stake-and-slash model ensures compliance: honest actors earn yield; misbehavior burns stake.

- **Zero Boilerplate**  
  One `send_intent()` call handles IPFS pinning, envelope creation, signing, gas estimation, and transaction submission.

- **Chain-Agnostic**  
  Compatible with any HTTPS RPC endpoint and EVM-compatible network (Ethereum, zkSync, Polygon, etc.).

- **Extensible Signing**  
  Use raw private keys, hardware wallets, KMS, or your own signer implementation via a simple `Signer` protocol.

---

## 🔧 Installation

Install from PyPI:

```bash
pip install intentlayer-sdk
```

For development or latest changes:

```bash
git clone https://github.com/intentlayer/intentlayer-sdk.git
cd intentlayer-sdk
pip install -e .
```

---

## 🎯 Quickstart

```python
import os
import time
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from intentlayer_sdk import (
    IntentClient, create_envelope,
    PinningError, EnvelopeError, TransactionError, NetworkError
)

# 1. Environment
PINNER_URL = os.getenv("PINNER_URL", "https://pin.example.com")
PRIVATE_KEY = os.getenv("PRIVATE_KEY")  # never commit this!

# 2. Initialize client using network configuration
client = IntentClient.from_network(
    network="zksync-era-sepolia",  # Network from networks.json
    pinner_url=PINNER_URL,
    signer=PRIVATE_KEY,  # Can be a private key string or a Signer instance
)

# Verify connected to the right chain
client.assert_chain_id()

# Query minimum stake from contract
min_stake = client.min_stake_wei
print(f"Minimum stake: {min_stake / 10**18} ETH")

# 3. Register a DID first (if not already registered)
did = "did:key:z6MkpzExampleDid"

try:
    # Check if DID already exists
    owner, active = client.resolve_did(did)
    if owner == "0x0000000000000000000000000000000000000000":
        # DID not registered yet - register it
        print(f"Registering DID: {did}")
        reg_receipt = client.register_did(did)
        print(f"DID registered: {client.tx_url(reg_receipt['transactionHash'])}")
    elif not active:
        # DID exists but inactive
        print(f"Reactivating DID: {did}")
        client.register_did(did, force=True)
    else:
        print(f"DID already registered to {owner}")
except Exception as e:
    print(f"Error checking/registering DID: {e}")

# 4. Create a signed envelope
prompt = "Translate 'hello' to French"
private_key = Ed25519PrivateKey.generate()  # For envelope signing

# Create full envelope with signature
envelope = create_envelope(
    prompt=prompt,
    model_id="gpt-4o@2025-03-12",
    tool_id="openai.chat",
    did=did,
    private_key=private_key,
    stake_wei=min_stake,
    timestamp_ms=int(time.time() * 1000)
)

# Get envelope hash for on-chain recording
envelope_hash = envelope.hex_hash()

# 5. Create payload with envelope
payload = {
    "prompt": prompt,
    "envelope": envelope.model_dump(),
    "metadata": {
        "user_id": "user123",
        "session_id": "session456"
    }
}

# 6. Record intent on-chain
try:
    receipt = client.send_intent(envelope_hash=envelope_hash, payload_dict=payload)
    tx_hash = receipt["transactionHash"]
    print(f"✔️ TxHash: {tx_hash}")
    print(f"✔️ Explorer: {client.tx_url(tx_hash)}")
except PinningError as e: print("IPFS error:", e)
except EnvelopeError as e: print("Envelope error:", e)
except NetworkError as e: print("Network error:", e)
except TransactionError as e: print("Tx failed:", e)
```

---

## 🔐 Security Best Practices

- **Never hard-code private keys** in source.  
- **Use environment variables**, hardware wallets, or managed key services (AWS KMS, HashiCorp Vault).  
- The SDK enforces HTTPS for RPC and pinner URLs in production (localhost/127.0.0.1 are exempt).

---

## 📚 High-Level API

### `IntentClient.from_network(...)`

| Parameter          | Type                 | Required             | Description                                              |
|--------------------|----------------------|----------------------|----------------------------------------------------------|
| `network`          | `str`                | Yes                  | Network name from networks.json (e.g., "zksync-era-sepolia") |
| `pinner_url`       | `str`                | Yes                  | IPFS pinner service URL                                  |
| `signer`           | `Union[str, Signer]` | Yes                  | Private key string or Signer instance                    |
| `rpc_url`          | `str`                | No                   | Override RPC URL from networks.json                      |
| `retry_count`      | `int` (default=3)    | No                   | HTTP retry attempts                                      |
| `timeout`          | `int` (default=30)   | No                   | Request timeout in seconds                               |
| `logger`           | `logging.Logger`     | No                   | Custom logger instance                                   |

### `IntentClient(...)` (Legacy constructor)

| Parameter          | Type                 | Required             | Description                                              |
|--------------------|----------------------|----------------------|----------------------------------------------------------|
| `rpc_url`          | `str`                | Yes                  | EVM RPC endpoint (must be `https://` in prod)           |
| `pinner_url`       | `str`                | Yes                  | IPFS pinner service URL                                  |
| `signer`           | `Signer`             | Yes                  | Signer implementing `.sign_transaction()`               |
| `recorder_address` | `str`                | Yes                  | Deployed `IntentRecorder` contract address               |
| `did_registry_address` | `str`            | No                   | DIDRegistry contract address (for DID operations)        |
| `retry_count`      | `int` (default=3)    | No                   | HTTP retry attempts                                      |
| `timeout`          | `int` (default=30)   | No                   | Request timeout in seconds                               |
| `logger`           | `logging.Logger`     | No                   | Custom logger instance                                   |

### Key Methods

#### `create_envelope(...) → CallEnvelope`

Creates a complete signed envelope for recording an intent.

```python
from intentlayer_sdk import create_envelope
envelope = create_envelope(
    prompt="What is the capital of France?",
    model_id="gpt-4o@2025-03-12",
    tool_id="openai.chat",
    did="did:key:z6MkpzExampleDid",
    private_key=private_key,  # Ed25519PrivateKey instance
    stake_wei=client.min_stake_wei
)
```

#### `send_intent(...) → Dict[str, Any]`

- **Pins** JSON to IPFS  
- **Builds** & **signs** a `recordIntent` transaction  
- **Sends** it on-chain and waits for a receipt  

#### `register_did(did, ...) → Dict[str, Any]`

Registers a DID with the DIDRegistry contract.

#### `resolve_did(did) → Tuple[str, bool]`

Resolves a DID to an address and active flag.

#### `assert_chain_id()`

Verifies the connected chain matches the expected chain ID.

#### `tx_url(tx_hash) → str`

Gets a block explorer URL for a transaction hash.

---

## ⚙️ Advanced Usage

### Custom Signer

```python
from web3 import Account

class VaultSigner:
    def __init__(self, address, vault_client):
        self.address = address
        self.vault   = vault_client

    def sign_transaction(self, tx):
        # fetch key from vault and sign
        return self.vault.sign(tx)

client = IntentClient(
    rpc_url         = "…",
    pinner_url      = "…",
    min_stake_wei   = 10**16,
    signer          = VaultSigner("0xYourAddr", my_vault),
    contract_address= "0x…"
)
```

---

## 🧪 Testing & Coverage

```bash
pytest --cov=intentlayer_sdk --cov-fail-under=80
```

We maintain ≥ 80% coverage to guarantee stability.

---

## 🤝 Contributing

1. Fork the repo  
2. Create a feature branch (`git checkout -b feature/...`)  
3. Commit your changes  
4. Open a pull request  

Please follow our [Code of Conduct](CODE_OF_CONDUCT.md) and [Contribution Guidelines](CONTRIBUTING.md).

---

## 📝 License

This project is licensed under the **MIT License**. See [LICENSE](LICENSE) for details.

