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
5 changes: 5 additions & 0 deletions .changelog/calm-lakes-wink.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
pytempo: major
---

Added TIP-1011 `authorizeKey` support with `KeyRestrictions` struct (T3+) as the new `authorize_key` method, and renamed the previous flat-params variant to `authorize_key_legacy` for pre-T3 compatibility. Updated `IAccountKeychain` ABI with the new function signature and `LegacyAuthorizeKeySelectorChanged` error.
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -196,3 +196,4 @@ jobs:
run: uv run pytest -v -s tests/test_integration.py
env:
TEMPO_RPC_URL: ${{ secrets.TEMPO_TESTNET_RPC_URL }}
TEMPO_HARDFORK: T2
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ data = as_bytes("0xabcdef") # -> b'\xab\xcd\xef'

### `TempoTransaction`

Immutable, strongly-typed transaction (frozen dataclass).
Immutable, strongly-typed transaction (frozen attrs model).

**Factory Methods:**

Expand All @@ -237,6 +237,7 @@ Immutable, strongly-typed transaction (frozen dataclass).
**Methods:**

- `sign(private_key, for_fee_payer=False)` - Sign transaction (returns new instance)
- `sign_access_key(access_key_private_key, root_account)` - Sign with access key (returns new instance)
- `encode()` - Encode to bytes for transmission
- `hash()` - Get transaction hash
- `get_signing_hash(for_fee_payer=False)` - Get hash to sign
Expand Down
56 changes: 45 additions & 11 deletions docs/guides/access-keys.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,18 @@ Create a {py:class}`~pytempo.KeyAuthorization`, sign it with the root account, a
```python
from pytempo import (
TempoTransaction, Call,
create_key_authorization, SignatureType,
KeyAuthorization, SignatureType, TokenLimit,
)

# Create authorization for a new access key
auth = create_key_authorization(
auth = KeyAuthorization(
key_id="0xAccessKeyAddress...",
chain_id=42429,
key_type=SignatureType.SECP256K1,
expiry=1893456000, # optional: expires ~2030
limits=[
{"token": "0xUSDCAddress...", "limit": 1000 * 10**6},
],
limits=(
TokenLimit(token="0xUSDCAddress...", limit=1000 * 10**6),
),
)

# Sign with root account
Expand All @@ -32,16 +32,16 @@ tx = TempoTransaction.create(
gas_limit=100_000,
max_fee_per_gas=2_000_000_000,
calls=(Call.create(to="0xRecipient...", value=1000),),
key_authorization=signed_auth.rlp_encode(),
key_authorization=signed_auth,
)
```

## Signing with an access key

Use {py:func}`~pytempo.sign_tx_access_key` to sign a transaction as an access key holder:
Use {py:meth}`~pytempo.TempoTransaction.sign_access_key` to sign a transaction as an access key holder:

```python
from pytempo import TempoTransaction, Call, sign_tx_access_key
from pytempo import TempoTransaction, Call

tx = TempoTransaction.create(
chain_id=42429,
Expand All @@ -50,8 +50,7 @@ tx = TempoTransaction.create(
calls=(Call.create(to="0xRecipient...", value=1000),),
)

signed_tx = sign_tx_access_key(
tx,
signed_tx = tx.sign_access_key(
access_key_private_key="0xAccessKeyPrivateKey...",
root_account="0xRootAccountAddress...",
)
Expand All @@ -78,9 +77,44 @@ remaining = AccountKeychain.get_remaining_limit(
print(f"Remaining: {remaining}")
```

## On-chain key authorization with call scopes (T3+)

For on-chain key provisioning via the AccountKeychain precompile, you can restrict which contracts and functions the key is allowed to call:

```python
from pytempo import CallScope, SignatureType
from pytempo.contracts import AccountKeychain, ALPHA_USD

call = AccountKeychain.authorize_key(
key_id="0xAccessKeyAddress...",
signature_type=SignatureType.SECP256K1,
expiry=2**64 - 1,
allow_any_calls=False,
allowed_calls=(
CallScope.transfer(target=ALPHA_USD),
CallScope.approve(target=ALPHA_USD),
),
)
```

Available call scope constructors:

- `CallScope.unrestricted(target=...)` — allow all functions on a target
- `CallScope.transfer(target=...)` — allow `transfer(address,uint256)` on a TIP20 token
- `CallScope.approve(target=...)` — allow `approve(address,uint256)` on a TIP20 token
- `CallScope.transfer_with_memo(target=...)` — allow `transferWithMemo(address,uint256,bytes32)` on a TIP20 token

```{note}
Before T3 is activated, pass ``legacy=True`` to use the pre-T3 encoding::

call = AccountKeychain.authorize_key(..., legacy=True)

Remove ``legacy=True`` once T3 is live.
```

## Signature types

The {py:class}`~pytempo.SignatureType` constants define supported key types:
The {py:class}`~pytempo.SignatureType` enum defines supported key types:

| Constant | Value | Description |
|---|---|---|
Expand Down
14 changes: 12 additions & 2 deletions pytempo/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@
INNER_SIGNATURE_LENGTH,
KEYCHAIN_SIGNATURE_LENGTH,
KEYCHAIN_SIGNATURE_TYPE,
CallScope,
KeyAuthorization,
KeychainSignature,
SignatureType,
SignedKeyAuthorization,
TokenLimit,
Expand All @@ -42,10 +44,13 @@
Address,
BytesLike,
Hash32,
Selector,
as_address,
as_bytes,
as_hash32,
as_optional_address,
as_selector,
validate_nonempty_address,
)

__version__ = "0.3.1"
Expand All @@ -54,11 +59,14 @@
# Types
"Address",
"Hash32",
"Selector",
"BytesLike",
"as_address",
"as_bytes",
"as_hash32",
"as_optional_address",
"as_selector",
"validate_nonempty_address",
# Models
"Call",
"AccessListItem",
Expand All @@ -72,11 +80,13 @@
"SignedKeyAuthorization",
"SignatureType",
"TokenLimit",
"create_key_authorization",
# Keychain signing
"CallScope",
"KeychainSignature",
# Keychain signing (deprecated free functions)
"KEYCHAIN_SIGNATURE_TYPE",
"KEYCHAIN_SIGNATURE_LENGTH",
"INNER_SIGNATURE_LENGTH",
"build_keychain_signature",
"create_key_authorization",
"sign_tx_access_key",
]
Loading
Loading