t402 1.7.1__tar.gz → 1.9.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {t402-1.7.1 → t402-1.9.0}/.gitignore +1 -0
- {t402-1.7.1 → t402-1.9.0}/PKG-INFO +1 -1
- {t402-1.7.1 → t402-1.9.0}/pyproject.toml +1 -1
- {t402-1.7.1 → t402-1.9.0}/src/t402/__init__.py +1 -1
- {t402-1.7.1 → t402-1.9.0}/src/t402/schemes/__init__.py +40 -1
- {t402-1.7.1 → t402-1.9.0}/src/t402/schemes/evm/__init__.py +21 -0
- t402-1.9.0/src/t402/schemes/evm/upto/__init__.py +58 -0
- t402-1.9.0/src/t402/schemes/evm/upto/client.py +240 -0
- t402-1.9.0/src/t402/schemes/evm/upto/types.py +305 -0
- t402-1.9.0/src/t402/schemes/upto/__init__.py +80 -0
- t402-1.9.0/src/t402/schemes/upto/types.py +376 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/types.py +1 -1
- t402-1.9.0/tests/test_upto.py +325 -0
- t402-1.9.0/tests/test_upto_evm.py +297 -0
- {t402-1.7.1 → t402-1.9.0}/.python-version +0 -0
- {t402-1.7.1 → t402-1.9.0}/README.md +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/bridge/__init__.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/bridge/client.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/bridge/constants.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/bridge/router.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/bridge/scan.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/bridge/types.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/chains.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/cli.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/clients/__init__.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/clients/base.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/clients/httpx.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/clients/requests.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/common.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/encoding.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/erc4337/__init__.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/erc4337/accounts.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/erc4337/bundlers.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/erc4337/paymasters.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/erc4337/types.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/evm_paywall_template.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/exact.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/facilitator.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/fastapi/__init__.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/fastapi/dependencies.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/fastapi/middleware.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/flask/__init__.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/flask/middleware.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/mcp/__init__.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/mcp/constants.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/mcp/server.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/mcp/tools.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/mcp/types.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/networks.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/path.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/paywall.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/py.typed +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/schemes/evm/exact/__init__.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/schemes/evm/exact/client.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/schemes/evm/exact/server.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/schemes/interfaces.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/schemes/registry.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/schemes/ton/__init__.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/schemes/ton/exact/__init__.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/schemes/ton/exact/client.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/schemes/ton/exact/server.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/schemes/tron/__init__.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/schemes/tron/exact/__init__.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/schemes/tron/exact/client.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/schemes/tron/exact/server.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/svm.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/svm_paywall_template.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/ton.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/ton_paywall_template.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/tron.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/wdk/__init__.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/wdk/chains.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/wdk/errors.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/wdk/signer.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/src/t402/wdk/types.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/tests/clients/__init__.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/tests/clients/test_base.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/tests/clients/test_httpx.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/tests/clients/test_requests.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/tests/fastapi_tests/__init__.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/tests/fastapi_tests/test_middleware.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/tests/flask_tests/__init__.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/tests/flask_tests/test_middleware.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/tests/test_bridge.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/tests/test_common.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/tests/test_encoding.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/tests/test_exact.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/tests/test_fastapi.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/tests/test_mcp.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/tests/test_paywall.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/tests/test_schemes.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/tests/test_svm.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/tests/test_ton.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/tests/test_tron.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/tests/test_types.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/tests/test_v2_types.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/tests/test_wdk.py +0 -0
- {t402-1.7.1 → t402-1.9.0}/uv.lock +0 -0
|
@@ -71,6 +71,27 @@ from t402.schemes.evm import (
|
|
|
71
71
|
ExactEvmClientScheme,
|
|
72
72
|
ExactEvmServerScheme,
|
|
73
73
|
EvmSigner,
|
|
74
|
+
# Upto EVM
|
|
75
|
+
UptoEvmClientScheme,
|
|
76
|
+
PermitSignature,
|
|
77
|
+
PermitAuthorization,
|
|
78
|
+
UptoEIP2612Payload,
|
|
79
|
+
UptoEvmExtra,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
# Upto Core Types
|
|
83
|
+
from t402.schemes.upto import (
|
|
84
|
+
SCHEME_UPTO,
|
|
85
|
+
UptoPaymentRequirements,
|
|
86
|
+
UptoExtra,
|
|
87
|
+
UptoSettlement,
|
|
88
|
+
UptoUsageDetails,
|
|
89
|
+
UptoSettlementResponse,
|
|
90
|
+
UptoValidationResult,
|
|
91
|
+
is_upto_payment_requirements,
|
|
92
|
+
is_valid_unit,
|
|
93
|
+
create_payment_requirements as create_upto_requirements,
|
|
94
|
+
create_settlement as create_upto_settlement,
|
|
74
95
|
)
|
|
75
96
|
|
|
76
97
|
# TON Schemes
|
|
@@ -110,10 +131,28 @@ __all__ = [
|
|
|
110
131
|
"get_server_registry",
|
|
111
132
|
"get_facilitator_registry",
|
|
112
133
|
"reset_global_registries",
|
|
113
|
-
# EVM Schemes
|
|
134
|
+
# EVM Exact Schemes
|
|
114
135
|
"ExactEvmClientScheme",
|
|
115
136
|
"ExactEvmServerScheme",
|
|
116
137
|
"EvmSigner",
|
|
138
|
+
# EVM Upto Schemes
|
|
139
|
+
"UptoEvmClientScheme",
|
|
140
|
+
"PermitSignature",
|
|
141
|
+
"PermitAuthorization",
|
|
142
|
+
"UptoEIP2612Payload",
|
|
143
|
+
"UptoEvmExtra",
|
|
144
|
+
# Upto Core Types
|
|
145
|
+
"SCHEME_UPTO",
|
|
146
|
+
"UptoPaymentRequirements",
|
|
147
|
+
"UptoExtra",
|
|
148
|
+
"UptoSettlement",
|
|
149
|
+
"UptoUsageDetails",
|
|
150
|
+
"UptoSettlementResponse",
|
|
151
|
+
"UptoValidationResult",
|
|
152
|
+
"is_upto_payment_requirements",
|
|
153
|
+
"is_valid_unit",
|
|
154
|
+
"create_upto_requirements",
|
|
155
|
+
"create_upto_settlement",
|
|
117
156
|
# TON Schemes
|
|
118
157
|
"ExactTonClientScheme",
|
|
119
158
|
"ExactTonServerScheme",
|
|
@@ -5,6 +5,7 @@ blockchains (Ethereum, Base, Avalanche, etc.).
|
|
|
5
5
|
|
|
6
6
|
Supported schemes:
|
|
7
7
|
- exact: EIP-3009 TransferWithAuthorization
|
|
8
|
+
- upto: EIP-2612 Permit (usage-based billing)
|
|
8
9
|
"""
|
|
9
10
|
|
|
10
11
|
from t402.schemes.evm.exact import (
|
|
@@ -15,6 +16,17 @@ from t402.schemes.evm.exact import (
|
|
|
15
16
|
SCHEME_EXACT,
|
|
16
17
|
)
|
|
17
18
|
|
|
19
|
+
from t402.schemes.evm.upto import (
|
|
20
|
+
UptoEvmClientScheme,
|
|
21
|
+
create_payment_nonce,
|
|
22
|
+
SCHEME_UPTO,
|
|
23
|
+
PermitSignature,
|
|
24
|
+
PermitAuthorization,
|
|
25
|
+
UptoEIP2612Payload,
|
|
26
|
+
UptoEvmExtra,
|
|
27
|
+
is_eip2612_payload,
|
|
28
|
+
)
|
|
29
|
+
|
|
18
30
|
__all__ = [
|
|
19
31
|
# Exact scheme
|
|
20
32
|
"ExactEvmClientScheme",
|
|
@@ -22,4 +34,13 @@ __all__ = [
|
|
|
22
34
|
"EvmSigner",
|
|
23
35
|
"create_nonce",
|
|
24
36
|
"SCHEME_EXACT",
|
|
37
|
+
# Upto scheme
|
|
38
|
+
"UptoEvmClientScheme",
|
|
39
|
+
"create_payment_nonce",
|
|
40
|
+
"SCHEME_UPTO",
|
|
41
|
+
"PermitSignature",
|
|
42
|
+
"PermitAuthorization",
|
|
43
|
+
"UptoEIP2612Payload",
|
|
44
|
+
"UptoEvmExtra",
|
|
45
|
+
"is_eip2612_payload",
|
|
25
46
|
]
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""EVM Up-To Payment Scheme.
|
|
2
|
+
|
|
3
|
+
This package provides the upto payment scheme implementation for EVM networks
|
|
4
|
+
using EIP-2612 Permit for gasless token approvals.
|
|
5
|
+
|
|
6
|
+
The upto scheme allows clients to authorize a maximum amount that can be
|
|
7
|
+
settled later based on actual usage.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from t402.schemes.evm.upto.types import (
|
|
11
|
+
# Type definitions
|
|
12
|
+
PERMIT_TYPES,
|
|
13
|
+
PERMIT_DOMAIN_TYPES,
|
|
14
|
+
# Models
|
|
15
|
+
PermitSignature,
|
|
16
|
+
PermitAuthorization,
|
|
17
|
+
UptoEIP2612Payload,
|
|
18
|
+
UptoCompactPayload,
|
|
19
|
+
UptoEvmExtra,
|
|
20
|
+
UptoEvmSettlement,
|
|
21
|
+
UptoEvmUsageDetails,
|
|
22
|
+
# Type guards
|
|
23
|
+
is_eip2612_payload,
|
|
24
|
+
# Helper functions
|
|
25
|
+
create_permit_domain,
|
|
26
|
+
create_permit_message,
|
|
27
|
+
payload_from_dict,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
from t402.schemes.evm.upto.client import (
|
|
31
|
+
UptoEvmClientScheme,
|
|
32
|
+
create_payment_nonce,
|
|
33
|
+
SCHEME_UPTO,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
__all__ = [
|
|
37
|
+
# Constants
|
|
38
|
+
"SCHEME_UPTO",
|
|
39
|
+
"PERMIT_TYPES",
|
|
40
|
+
"PERMIT_DOMAIN_TYPES",
|
|
41
|
+
# Client
|
|
42
|
+
"UptoEvmClientScheme",
|
|
43
|
+
"create_payment_nonce",
|
|
44
|
+
# Types
|
|
45
|
+
"PermitSignature",
|
|
46
|
+
"PermitAuthorization",
|
|
47
|
+
"UptoEIP2612Payload",
|
|
48
|
+
"UptoCompactPayload",
|
|
49
|
+
"UptoEvmExtra",
|
|
50
|
+
"UptoEvmSettlement",
|
|
51
|
+
"UptoEvmUsageDetails",
|
|
52
|
+
# Type guards
|
|
53
|
+
"is_eip2612_payload",
|
|
54
|
+
# Helper functions
|
|
55
|
+
"create_permit_domain",
|
|
56
|
+
"create_permit_message",
|
|
57
|
+
"payload_from_dict",
|
|
58
|
+
]
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
"""EVM Up-To Scheme - Client Implementation.
|
|
2
|
+
|
|
3
|
+
This module provides the client-side implementation of the upto payment scheme
|
|
4
|
+
for EVM networks using EIP-2612 Permit.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import secrets
|
|
10
|
+
import time
|
|
11
|
+
from typing import Any, Dict, Optional, Union
|
|
12
|
+
|
|
13
|
+
from t402.types import PaymentRequirementsV2
|
|
14
|
+
from t402.chains import get_chain_id
|
|
15
|
+
from t402.schemes.evm.exact.client import EvmSigner
|
|
16
|
+
from t402.schemes.upto.types import UptoPaymentRequirements
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# Constants
|
|
20
|
+
SCHEME_UPTO = "upto"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def create_payment_nonce() -> bytes:
|
|
24
|
+
"""Create a random 32-byte payment nonce."""
|
|
25
|
+
return secrets.token_bytes(32)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class UptoEvmClientScheme:
|
|
29
|
+
"""Client scheme for EVM upto payments using EIP-2612.
|
|
30
|
+
|
|
31
|
+
This scheme creates payment payloads using EIP-2612 Permit,
|
|
32
|
+
which allows gasless token approvals for up-to payments.
|
|
33
|
+
|
|
34
|
+
Example:
|
|
35
|
+
```python
|
|
36
|
+
from eth_account import Account
|
|
37
|
+
|
|
38
|
+
# Create signer from private key
|
|
39
|
+
account = Account.from_key("0x...")
|
|
40
|
+
|
|
41
|
+
# Create scheme
|
|
42
|
+
scheme = UptoEvmClientScheme(account)
|
|
43
|
+
|
|
44
|
+
# Create payment payload
|
|
45
|
+
payload = await scheme.create_payment_payload(
|
|
46
|
+
t402_version=2,
|
|
47
|
+
requirements=requirements,
|
|
48
|
+
)
|
|
49
|
+
```
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
scheme = SCHEME_UPTO
|
|
53
|
+
caip_family = "eip155:*"
|
|
54
|
+
|
|
55
|
+
def __init__(
|
|
56
|
+
self,
|
|
57
|
+
signer: EvmSigner,
|
|
58
|
+
router_address: Optional[str] = None,
|
|
59
|
+
):
|
|
60
|
+
"""Initialize with an EVM signer.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
signer: Any object implementing the EvmSigner protocol
|
|
64
|
+
router_address: Optional default router contract address
|
|
65
|
+
"""
|
|
66
|
+
self._signer = signer
|
|
67
|
+
self._router_address = router_address
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def address(self) -> str:
|
|
71
|
+
"""Get the signer's address."""
|
|
72
|
+
return self._signer.address
|
|
73
|
+
|
|
74
|
+
async def create_payment_payload(
|
|
75
|
+
self,
|
|
76
|
+
t402_version: int,
|
|
77
|
+
requirements: Union[UptoPaymentRequirements, PaymentRequirementsV2, Dict[str, Any]],
|
|
78
|
+
) -> Dict[str, Any]:
|
|
79
|
+
"""Create a payment payload for EVM upto scheme.
|
|
80
|
+
|
|
81
|
+
Creates an EIP-2612 Permit authorization and signs it.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
t402_version: Protocol version (1 or 2)
|
|
85
|
+
requirements: Payment requirements with maxAmount, asset, payTo, etc.
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
Dict with t402Version and payload containing permit signature
|
|
89
|
+
and authorization data.
|
|
90
|
+
"""
|
|
91
|
+
# Extract requirements (handle both model and dict)
|
|
92
|
+
if hasattr(requirements, "model_dump"):
|
|
93
|
+
req = requirements.model_dump(by_alias=True)
|
|
94
|
+
else:
|
|
95
|
+
req = dict(requirements)
|
|
96
|
+
|
|
97
|
+
# Get network and chain ID
|
|
98
|
+
network = req.get("network", "")
|
|
99
|
+
chain_id = self._get_chain_id(network)
|
|
100
|
+
|
|
101
|
+
# Get maxAmount for upto scheme
|
|
102
|
+
max_amount = req.get("maxAmount") or req.get("max_amount", "0")
|
|
103
|
+
|
|
104
|
+
# Get router/spender address
|
|
105
|
+
extra = req.get("extra", {})
|
|
106
|
+
router_address = (
|
|
107
|
+
extra.get("routerAddress")
|
|
108
|
+
or extra.get("router_address")
|
|
109
|
+
or self._router_address
|
|
110
|
+
or req.get("payTo") # Fallback to payTo
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
# Get asset address
|
|
114
|
+
asset = req.get("asset", "")
|
|
115
|
+
|
|
116
|
+
# Get timeout
|
|
117
|
+
max_timeout = req.get("maxTimeoutSeconds") or req.get("max_timeout_seconds", 300)
|
|
118
|
+
|
|
119
|
+
# Get EIP-712 domain info
|
|
120
|
+
token_name = extra.get("name", "USD Coin")
|
|
121
|
+
token_version = extra.get("version", "2")
|
|
122
|
+
|
|
123
|
+
# Create payment nonce
|
|
124
|
+
payment_nonce = create_payment_nonce()
|
|
125
|
+
|
|
126
|
+
# Calculate deadline
|
|
127
|
+
deadline = int(time.time()) + max_timeout
|
|
128
|
+
|
|
129
|
+
# Get permit nonce from token contract (would need RPC call in production)
|
|
130
|
+
# For now, use 0 as placeholder - real implementation needs contract call
|
|
131
|
+
permit_nonce = 0
|
|
132
|
+
|
|
133
|
+
# Create authorization
|
|
134
|
+
authorization = {
|
|
135
|
+
"owner": self._signer.address,
|
|
136
|
+
"spender": router_address,
|
|
137
|
+
"value": str(max_amount),
|
|
138
|
+
"deadline": str(deadline),
|
|
139
|
+
"nonce": permit_nonce,
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
# Sign the permit
|
|
143
|
+
signature = self._sign_permit(
|
|
144
|
+
authorization=authorization,
|
|
145
|
+
chain_id=chain_id,
|
|
146
|
+
asset_address=asset,
|
|
147
|
+
token_name=token_name,
|
|
148
|
+
token_version=token_version,
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
# Build payload
|
|
152
|
+
payload = {
|
|
153
|
+
"signature": signature,
|
|
154
|
+
"authorization": authorization,
|
|
155
|
+
"paymentNonce": f"0x{payment_nonce.hex()}",
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
"t402Version": t402_version,
|
|
160
|
+
"payload": payload,
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
def _get_chain_id(self, network: str) -> int:
|
|
164
|
+
"""Get chain ID from network identifier."""
|
|
165
|
+
if network.startswith("eip155:"):
|
|
166
|
+
return int(network.split(":")[1])
|
|
167
|
+
|
|
168
|
+
try:
|
|
169
|
+
return get_chain_id(network)
|
|
170
|
+
except (KeyError, ValueError):
|
|
171
|
+
raise ValueError(f"Unknown network: {network}")
|
|
172
|
+
|
|
173
|
+
def _sign_permit(
|
|
174
|
+
self,
|
|
175
|
+
authorization: Dict[str, Any],
|
|
176
|
+
chain_id: int,
|
|
177
|
+
asset_address: str,
|
|
178
|
+
token_name: str,
|
|
179
|
+
token_version: str,
|
|
180
|
+
) -> Dict[str, Any]:
|
|
181
|
+
"""Sign an EIP-2612 Permit.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
authorization: Permit authorization data
|
|
185
|
+
chain_id: EVM chain ID
|
|
186
|
+
asset_address: Token contract address
|
|
187
|
+
token_name: Token name for EIP-712 domain
|
|
188
|
+
token_version: Token version for EIP-712 domain
|
|
189
|
+
|
|
190
|
+
Returns:
|
|
191
|
+
Signature as dict with v, r, s components
|
|
192
|
+
"""
|
|
193
|
+
# Build EIP-712 typed data
|
|
194
|
+
domain = {
|
|
195
|
+
"name": token_name,
|
|
196
|
+
"version": token_version,
|
|
197
|
+
"chainId": chain_id,
|
|
198
|
+
"verifyingContract": asset_address,
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
types = {
|
|
202
|
+
"Permit": [
|
|
203
|
+
{"name": "owner", "type": "address"},
|
|
204
|
+
{"name": "spender", "type": "address"},
|
|
205
|
+
{"name": "value", "type": "uint256"},
|
|
206
|
+
{"name": "nonce", "type": "uint256"},
|
|
207
|
+
{"name": "deadline", "type": "uint256"},
|
|
208
|
+
]
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
message = {
|
|
212
|
+
"owner": authorization["owner"],
|
|
213
|
+
"spender": authorization["spender"],
|
|
214
|
+
"value": int(authorization["value"]),
|
|
215
|
+
"nonce": authorization["nonce"],
|
|
216
|
+
"deadline": int(authorization["deadline"]),
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
# Sign
|
|
220
|
+
signed = self._signer.sign_typed_data(
|
|
221
|
+
domain_data=domain,
|
|
222
|
+
message_types=types,
|
|
223
|
+
message_data=message,
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
# Extract signature components
|
|
227
|
+
sig_hex = signed.signature.hex()
|
|
228
|
+
if sig_hex.startswith("0x"):
|
|
229
|
+
sig_hex = sig_hex[2:]
|
|
230
|
+
|
|
231
|
+
# Split into v, r, s
|
|
232
|
+
r = f"0x{sig_hex[:64]}"
|
|
233
|
+
s = f"0x{sig_hex[64:128]}"
|
|
234
|
+
v = int(sig_hex[128:], 16) if len(sig_hex) > 128 else 27
|
|
235
|
+
|
|
236
|
+
return {
|
|
237
|
+
"v": v,
|
|
238
|
+
"r": r,
|
|
239
|
+
"s": s,
|
|
240
|
+
}
|