Skip to content

Commit 761c544

Browse files
improve readme, add github ci workflow
1 parent 5f653c0 commit 761c544

File tree

5 files changed

+198
-92
lines changed

5 files changed

+198
-92
lines changed

β€Ž.github/workflows/ci.ymlβ€Ž

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request:
7+
branches: [ main ]
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
strategy:
13+
matrix:
14+
python-version: [3.12, 3.14]
15+
16+
steps:
17+
- name: Checkout repository
18+
uses: actions/checkout@v4
19+
20+
- name: Set up Python
21+
uses: actions/setup-python@v4
22+
with:
23+
python-version: ${{ matrix.python-version }}
24+
25+
- name: Cache pip
26+
uses: actions/cache@v4
27+
with:
28+
path: ~/.cache/pip
29+
key: ${{ runner.os }}-pip-${{ hashFiles('**/pyproject.toml') }}
30+
restore-keys: |
31+
${{ runner.os }}-pip-
32+
33+
- name: Upgrade pip
34+
run: python -m pip install --upgrade pip
35+
36+
- name: Install project (editable) and test deps
37+
run: |
38+
python -m pip install -e .
39+
python -m pip install pytest pytest-xdist
40+
41+
- name: Run tests (parallel if xdist available)
42+
run: python -m pytest -n auto -q

β€ŽREADME.mdβ€Ž

Lines changed: 63 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# Arkiv Python Starter
1+
# Arkiv - The Web3 Database - Python Starter
22

3-
**Get started with Arkiv in under 2 minutes!** πŸš€
3+
**Get started with Arkiv in under 5 minutes!** πŸš€
44

55
This starter template provides everything you need to build applications with the Arkiv SDK for Python. No complex setup requiredβ€”just clone, open, and run.
66

@@ -32,14 +32,14 @@ Store, query, and manage data on-chain with the simplicity of a traditional data
3232
- ⚑ **Real-time Events** - Subscribe to data changes as they happen
3333
- πŸ”— **Web3 Compatible** - Just a simple extension of the web3.py library
3434

35-
**Prerequisites:**
36-
- βœ… Git
37-
- βœ… Docker
38-
- βœ… VS Code
39-
- βœ… GitHub Copilot (optional but recommended)
40-
4135
## Quick Start
4236

37+
**Before you begin, make sure you have:**
38+
- Git
39+
- Docker (running)
40+
- VS Code with Dev Containers extension
41+
- GitHub Copilot (optional, but recommended for learning)
42+
4343
### 1. Clone and Open
4444

4545
```bash
@@ -93,7 +93,6 @@ Every entity has three core components:
9393
The actual data you want to store on-chain.
9494

9595
- **Type:** Raw bytes (`bytes` in Python)
96-
- **Size:** Up to approx 100KB per entity
9796
- **Format:** Can be anythingβ€”text, JSON, binary data, serialized objects
9897
- **Example:**
9998
```python
@@ -190,11 +189,58 @@ How long the entity should persist on-chain before automatic expiration.
190189

191190
This design gives you the flexibility of a document database with the immutability and transparency of blockchain storage.
192191

192+
### Transaction Size Limits
193+
194+
Arkiv operations are blockchain transactions, and the **maximum transaction size** is determined by the underlying blockchain network (currently around **120KB** for the entire transaction).
195+
196+
This limit applies to the **complete transaction data**, which includes:
197+
- Entity payload(s)
198+
- All entity attributes (both system and custom)
199+
- Transaction metadata (signatures, gas data, etc.)
200+
- Multiple entities if you're creating/updating several in one transaction
201+
202+
**Practical guidelines:**
203+
- Single entity with large payload: ~90KB payload is safe (leaves room for attributes and metadata)
204+
- Multiple entities in one transaction: Total size of all payloads + attributes must fit within limit
205+
- Rich attributes: Many custom attributes reduce available space for payload
206+
- Batch operations: Consider transaction size when batching multiple entity modifications
207+
208+
**Example:**
209+
```python
210+
# This is fine - single entity with reasonable payload
211+
entity_key, receipt = client.arkiv.create_entity(
212+
payload=json.dumps({"data": "..." * 1000}).encode(), # ~80KB
213+
attributes={"type": "large_document"}
214+
)
215+
216+
# This may fail - total size exceeds transaction limit
217+
# (payload + attributes + metadata > 100KB)
218+
large_payload = b"x" * 95000 # 95KB payload
219+
many_attributes = {f"attr_{i}": f"value_{i}" for i in range(1000)}
220+
entity_key, receipt = client.arkiv.create_entity(
221+
payload=large_payload,
222+
attributes=many_attributes # Too much data!
223+
)
224+
```
225+
193226
## Examples
194227

195228
The template includes 4 progressive tutorials, each building on the previous:
196229

197-
### Example 1: Basic CRUD Operations (5 min)
230+
1. Basic Arkiv CRUD operations
231+
2. Querying entities
232+
3. Real-time entity events
233+
4. Web3 integration
234+
235+
Each example:
236+
- Starts a local Arkiv node, running in a container without external dependencies
237+
- Creates and funds a test account
238+
- Demonstrates specific features
239+
- Cleans up automatically and stops the local node when done
240+
241+
All examples are self-contained and can run independently as modules: `uv run python -m arkiv_starter.01_basic_crud`
242+
243+
### Example 1: Arkiv CRUD Operations (5 min)
198244
**File:** `01_basic_crud.py`
199245

200246
Learn the fundamentals:
@@ -222,7 +268,7 @@ Master data retrieval:
222268
uv run python -m arkiv_starter.02_queries
223269
```
224270

225-
### Example 3: Blockchain Events (10 min)
271+
### Example 3: Real-Time Events (10 min)
226272
**File:** `03_events.py`
227273

228274
Real-time data monitoring:
@@ -249,18 +295,6 @@ Advanced usage:
249295
uv run python -m arkiv_starter.04_web3_integration
250296
```
251297

252-
253-
254-
## How It Works
255-
256-
Each example:
257-
1. **Starts a local Arkiv node** - Runs in Docker, no external dependencies
258-
2. **Creates and funds a test account** - Ready to transact immediately
259-
3. **Demonstrates specific features** - Focused, runnable code
260-
4. **Cleans up automatically** - Stops the node when done
261-
262-
All examples are self-contained and can run independently as modules: `uv run python -m arkiv_starter.01_basic_crud`
263-
264298
---
265299

266300
## Working with AI Assistants
@@ -463,6 +497,12 @@ The starter includes automated tests:
463497
uv run pytest
464498
```
465499

500+
For faster parallel execution:
501+
502+
```bash
503+
uv run pytest -n auto
504+
```
505+
466506
This verifies:
467507
- βœ… Basic CRUD operations work correctly
468508
- βœ… Query functionality performs as expected

β€Žpyproject.tomlβ€Ž

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ packages = ["src/arkiv_starter"]
1818
[dependency-groups]
1919
dev = [
2020
"pytest>=8.0.0",
21+
"pytest-xdist>=3.0.0",
2122
]
2223

2324
[tool.pytest.ini_options]

β€Žsrc/arkiv_starter/04_web3_integration.pyβ€Ž

Lines changed: 65 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -5,29 +5,33 @@
55
- Using Arkiv with existing web3.py code
66
- Accessing raw Web3 functionality
77
- Mixing Arkiv operations with standard Web3 calls
8+
- Full CRUD lifecycle (create, read, update, delete)
89
9-
Run this example: python examples/04_web3_integration.py
10+
Run this example: uv run python -m arkiv_starter.04_web3_integration
1011
"""
1112

1213
from arkiv.provider import ProviderBuilder
13-
from eth_account import Account
14-
from web3 import Web3
15-
16-
from arkiv import Arkiv
14+
from arkiv import Arkiv, NamedAccount
1715
from arkiv.node import ArkivNode
16+
from web3 import Web3
17+
from typing import cast
18+
from web3.providers.base import BaseProvider
1819

1920
# Setup: Start node and create client
2021
print("πŸš€ Starting local Arkiv node...")
2122
node = ArkivNode()
2223
node.start()
2324

25+
# Create and fund account
2426
provider = ProviderBuilder().node(node).build()
25-
client = Arkiv(provider)
27+
provider = cast(BaseProvider, provider) # cast for static type checkers
2628

2729
# Create and fund account
28-
account = Account.create()
30+
account = NamedAccount.create("web3-integration")
2931
node.fund_account(account)
30-
client.eth.default_account = account
32+
33+
# Initialize client with account
34+
client = Arkiv(provider, account=account)
3135
print(f"βœ… Account ready: {account.address}\n")
3236

3337
# ============================================================================
@@ -61,19 +65,15 @@
6165

6266
# Create entity
6367
print("πŸ“ Creating entity with Arkiv...")
64-
tx_hash = client.arkiv.create_entity(
65-
payload=b"Web3 integration example", expires_in=3600, content_type="text/plain"
68+
entity_key, receipt = client.arkiv.create_entity(
69+
payload=b"Web3 integration example",
70+
expires_in=3600,
71+
content_type="text/plain"
6672
)
67-
print(f" Transaction Hash: {tx_hash.hex()}")
68-
69-
# Wait and get receipt (standard Web3 call)
70-
receipt = client.eth.wait_for_transaction_receipt(tx_hash)
71-
print(f" Block Number: {receipt['blockNumber']}")
72-
print(f" Gas Used: {receipt['gasUsed']}\n")
73+
print(f" Transaction Hash: {receipt.tx_hash}")
74+
print(f" Block Number: {receipt.block_number}\n")
7375

74-
# Extract entity ID from logs
75-
entity_id = int(receipt["logs"][0]["topics"][1].hex(), 16)
76-
print(f"βœ… Entity Created: ID {entity_id}\n")
76+
print(f"βœ… Entity Created: Key {entity_key}\n")
7777

7878
# ============================================================================
7979
# Part 3: Direct Contract Interaction (Advanced)
@@ -85,70 +85,67 @@
8585
arkiv_contract = client.arkiv.contract
8686
print(f"πŸ“œ Contract Address: {arkiv_contract.address}")
8787

88-
# Call contract methods directly
89-
entity_count = arkiv_contract.functions.getEntityCount().call()
90-
print(f"πŸ“Š Total Entity Count: {entity_count}")
91-
92-
# Get entity using direct contract call
93-
entity_data = arkiv_contract.functions.getEntity(entity_id).call()
94-
print(f"\nπŸ“– Entity {entity_id} (via direct contract call):")
95-
print(f" Owner: {entity_data[0]}")
96-
print(f" Content Type: {entity_data[1]}")
97-
print(f" Expires At: {entity_data[2]}")
98-
print(f" Content Length: {len(entity_data[3])} bytes")
99-
print(f" Content: {entity_data[3].decode('utf-8')}\n")
88+
# Get entity using Arkiv's high-level API
89+
entity = client.arkiv.get_entity(entity_key)
90+
print(f"\nπŸ“– Entity {entity_key} (via Arkiv SDK):")
91+
print(f" Owner: {entity.owner}")
92+
print(f" Content Type: {entity.content_type}")
93+
print(f" Expires At Block: {entity.expires_at_block}")
94+
if entity.payload:
95+
print(f" Content Length: {len(entity.payload)} bytes")
96+
print(f" Content: {entity.payload.decode('utf-8')}\n")
97+
else:
98+
print(f" No payload content\n")
10099

101100
# ============================================================================
102-
# Part 4: Transaction Signing (Manual)
101+
# Part 4: Updating and Deleting Entities
103102
# ============================================================================
104-
print("✍️ Part 4: Manual Transaction Signing")
103+
print("✍️ Part 4: Updating and Deleting Entities")
105104
print("=" * 60)
106105

107-
# Build transaction manually
108-
print("πŸ”¨ Building transaction manually...")
109-
nonce = client.eth.get_transaction_count(account.address)
110-
111-
# Prepare update transaction
112-
update_txn = arkiv_contract.functions.updateEntity(
113-
entity_id,
114-
b"Manually signed update",
115-
int(client.eth.get_block("latest")["number"]) + 1800, # expires_at in blocks
116-
"text/plain",
117-
).build_transaction(
118-
{
119-
"from": account.address,
120-
"nonce": nonce,
121-
"gas": 200000,
122-
"gasPrice": client.eth.gas_price,
123-
}
106+
# Update entity
107+
print("πŸ”„ Updating entity...")
108+
update_receipt = client.arkiv.update_entity(
109+
entity_key,
110+
payload=b"Updated via Web3 example",
111+
expires_in=7200,
112+
content_type="text/plain"
124113
)
125-
126-
# Sign transaction
127-
print("✍️ Signing transaction...")
128-
signed_txn = client.eth.account.sign_transaction(update_txn, account.key)
129-
130-
# Send signed transaction
131-
print("πŸ“€ Sending signed transaction...")
132-
tx_hash = client.eth.send_raw_transaction(signed_txn.raw_transaction)
133-
print(f" Transaction Hash: {tx_hash.hex()}")
134-
135-
# Wait for confirmation
136-
receipt = client.eth.wait_for_transaction_receipt(tx_hash)
137-
print(f"βœ… Transaction mined in block {receipt['blockNumber']}\n")
114+
print(f" Transaction Hash: {update_receipt.tx_hash}")
115+
print(f" Block Number: {update_receipt.block_number}\n")
138116

139117
# Verify update
140-
updated_entity = client.arkiv.get_entity(entity_id)
141-
print(f"πŸ“– Updated content: {updated_entity.content.decode('utf-8')}")
118+
updated_entity = client.arkiv.get_entity(entity_key)
119+
if updated_entity.payload:
120+
print(f"πŸ“– Updated content: {updated_entity.payload.decode('utf-8')}\n")
121+
else:
122+
print(f"πŸ“– No payload content\n")
123+
124+
# Delete entity
125+
print("πŸ—‘οΈ Deleting entity...")
126+
delete_receipt = client.arkiv.delete_entity(entity_key)
127+
print(f" Transaction Hash: {delete_receipt.tx_hash}")
128+
print(f" Block Number: {delete_receipt.block_number}\n")
129+
130+
# Verify deletion (should return None or raise exception)
131+
try:
132+
deleted_entity = client.arkiv.get_entity(entity_key)
133+
if deleted_entity:
134+
print(f"⚠️ Entity still exists")
135+
else:
136+
print(f"βœ… Entity successfully deleted\n")
137+
except Exception as e:
138+
print(f"βœ… Entity successfully deleted (not found)\n")
142139

143140
# ============================================================================
144141
# Summary
145142
# ============================================================================
146-
print("\n" + "=" * 60)
143+
print("=" * 60)
147144
print("πŸ“‹ Summary:")
148145
print(" βœ… Standard Web3 operations work seamlessly")
149146
print(" βœ… Arkiv adds convenient entity management methods")
150-
print(" βœ… Direct contract access available when needed")
151-
print(" βœ… Full control over transaction signing")
147+
print(" βœ… Full CRUD operations on blockchain entities")
148+
print(" βœ… Access to contract details and chain information")
152149
print("=" * 60)
153150

154151
# Cleanup

0 commit comments

Comments
Β (0)