Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/smoke.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ on:
jobs:
smoke:
name: Testnet Smoke Tests
# The v0.19 line intentionally targets the resolution-kernel train and
# does not support the currently deployed pre-train testnets. Its live
# coverage comes from the cross-repository E2E stack until they upgrade.
if: github.event_name != 'pull_request' || github.base_ref != 'v0.19-dev'
runs-on: ubuntu-latest
# Bradbury/Asimov RPC reads occasionally stall for 2-3 minutes on
# single calls (testnet capacity); with 17 testnet-flagged tests
Expand Down
80 changes: 65 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ To install the GenLayerPY SDK, use the following command:
$ pip install genlayer-py
```

SDK releases follow their corresponding GenLayer protocol release. This
release targets the current resolution-kernel train; use the matching older SDK
release when connecting to an older deployment.

Here’s how to initialize the client and connect to the GenLayer Simulator:

### Reading a Transaction
Expand All @@ -44,21 +48,20 @@ transaction = client.get_transaction(hash=transaction_hash)
```python
from genlayer_py import create_client
from genlayer_py.chains import localnet
from genlayer_py.types import TransactionStatus

client = create_client(chain=localnet)

# Get simplified receipt (default - removes binary data, keeps execution results)
receipt = client.wait_for_transaction_receipt(
transaction_hash="0x...",
status=TransactionStatus.FINALIZED,
wait_until="finalized",
full_transaction=False # Default - simplified for readability
)

# Get complete receipt with all fields
full_receipt = client.wait_for_transaction_receipt(
transaction_hash="0x...",
status=TransactionStatus.FINALIZED,
wait_until="finalized",
full_transaction=True # Complete receipt with all internal data
)
```
Expand Down Expand Up @@ -101,7 +104,7 @@ transaction_hash = client.write_contract(
)
receipt = client.wait_for_transaction_receipt(
hash=transaction_hash,
status=TransactionStatus.FINALIZED, // or ACCEPTED
wait_until="finalized",
full_transaction=False // False by default - returns simplified receipt for better readability
)
```
Expand All @@ -117,7 +120,6 @@ estimate = client.estimate_transaction_fees(
{
"leaderTimeunitsAllocation": 100,
"validatorTimeunitsAllocation": 200,
"rotations": [0],
}
)

Expand All @@ -133,6 +135,11 @@ tx_hash = client.write_contract(
)
```

When `rotations` is omitted, estimates fund
`chain.default_consensus_max_rotations` for the initial round and every enabled
appeal round. Pass an explicit list, including `[0]`, when the application wants
to fund a different number of rotations.

If `fees["distribution"]` is provided without `feeValue`, the SDK derives the
fee deposit from FeeManager on network backends, or from `sim_getFeeConfig` on
Studio. Use `messageAllocations` with `estimate_transaction_fees` for
Expand Down Expand Up @@ -254,18 +261,26 @@ client.top_up_fees(
},
)

client.top_up_and_submit_appeal(
transaction_id=tx_hash,
value=1_400,
distribution={
"appealRounds": 1,
"rotations": [0, 0],
},
)
quote = client.get_appeal_quote(tx_hash)
if client.can_appeal(tx_hash, expected_decision_id=quote["decision_id"]):
client.top_up_and_submit_appeal(
transaction_id=tx_hash,
expected_decision_id=quote["decision_id"],
value=quote["total"],
distribution={
"appealRounds": 1,
"rotations": [0, 0],
},
)
```

`top_up_fees` returns the backend RPC hash. On network backends this is the EVM
transaction hash; on Studio/localnet it is the target GenLayer transaction id.
Appeal commands are guarded by the quoted decision id so a stale request cannot
bind to a newer decision. If the id and value are omitted, the SDK refreshes
this lightweight quote automatically. This applies to deployed Consensus.
Current Studio uses its native decision-free appeal methods: pass ``value``
explicitly and omit ``expected_decision_id``.

### Checking execution results

Expand All @@ -274,13 +289,13 @@ A transaction can be finalized by consensus but still have a failed execution. A
```python
from genlayer_py import create_client, create_account
from genlayer_py.chains import testnet_bradbury
from genlayer_py.types import TransactionStatus, ExecutionResult
from genlayer_py.types import ExecutionResult

client = create_client(chain=testnet_bradbury, account=create_account())

receipt = client.wait_for_transaction_receipt(
transaction_hash=tx_hash,
status=TransactionStatus.FINALIZED,
wait_until="finalized",
)

if receipt.get("tx_execution_result_name") == ExecutionResult.FINISHED_WITH_RETURN.value:
Expand All @@ -305,6 +320,27 @@ Transactions can emit messages to other contracts. These messages create new chi
```python
tx = client.get_transaction(transaction_hash=tx_hash)

# The default lifecycle is derived only from stored chain state.
print(tx["lifecycle"])
# {"state": "processing", "phase": "revealing"}
# {"state": "decided", "outcome": "accepted"}

# Protocol projection/action details are available only through the explicit
# advanced API.
raw_lifecycle = client.get_transaction_lifecycle(transaction_hash=tx_hash)
print(raw_lifecycle["stored_status_name"])
print(raw_lifecycle["projected_status_name"])
print(raw_lifecycle["resolution_action_name"])
print(raw_lifecycle["resolution_source_name"])
# `resolution_action_name == "Finalize"` is the authoritative readiness verdict.
# On current Studio without the advanced lifecycle RPC, only stored status is
# provable; projection repeats it and resolution/decision fields stay inactive.

# The train stores the execution hash, not the old receipt bytes.
print(tx["tx_execution_hash"])
# `tx_receipt` remains present but is `None` when the
# protocol cannot supply the old bytes.

# Messages emitted by the contract during execution
print(tx["messages"])
# [{"messageType": 1, "recipient": "0x...", "value": 0, "data": "0x...", "onAcceptance": True, "saltNonce": 0}, ...]
Expand All @@ -315,6 +351,20 @@ print(child_tx_ids)
# ["0xabc...", "0xdef..."]
```

### Active and joined validators

The active set contains only validators currently eligible for protocol
duties. The joined registry is broader and can include validators that are not
yet selectable, are under-staked, or are otherwise unavailable.

```python
active = client.active_validators()
active_count = client.active_validators_count()

joined = client.joined_validators()
joined_count = client.joined_validators_count()
```

### Debugging transaction execution

Use `debug_trace_transaction` to inspect the full execution trace of a transaction, including return data, errors, and GenVM logs:
Expand Down
Loading
Loading