t402 1.9.1__py3-none-any.whl → 1.10.0__py3-none-any.whl
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/__init__.py +1 -1
- t402/a2a/__init__.py +73 -0
- t402/a2a/helpers.py +158 -0
- t402/a2a/types.py +145 -0
- t402/bridge/constants.py +1 -1
- t402/django/__init__.py +42 -0
- t402/django/middleware.py +596 -0
- t402/errors.py +213 -0
- t402/facilitator.py +125 -0
- t402/mcp/constants.py +3 -6
- t402/mcp/server.py +428 -44
- t402/mcp/web3_utils.py +493 -0
- t402/multisig/__init__.py +120 -0
- t402/multisig/constants.py +54 -0
- t402/multisig/safe.py +441 -0
- t402/multisig/signature.py +228 -0
- t402/multisig/transaction.py +238 -0
- t402/multisig/types.py +108 -0
- t402/multisig/utils.py +77 -0
- t402/schemes/__init__.py +19 -0
- t402/schemes/cosmos/__init__.py +114 -0
- t402/schemes/cosmos/constants.py +211 -0
- t402/schemes/cosmos/exact_direct/__init__.py +21 -0
- t402/schemes/cosmos/exact_direct/client.py +198 -0
- t402/schemes/cosmos/exact_direct/facilitator.py +493 -0
- t402/schemes/cosmos/exact_direct/server.py +315 -0
- t402/schemes/cosmos/types.py +501 -0
- t402/schemes/evm/__init__.py +1 -1
- t402/schemes/evm/exact_legacy/server.py +1 -1
- t402/schemes/near/__init__.py +25 -0
- t402/schemes/near/upto/__init__.py +54 -0
- t402/schemes/near/upto/types.py +272 -0
- t402/schemes/svm/__init__.py +15 -0
- t402/schemes/svm/upto/__init__.py +23 -0
- t402/schemes/svm/upto/types.py +193 -0
- t402/schemes/ton/__init__.py +15 -0
- t402/schemes/ton/upto/__init__.py +31 -0
- t402/schemes/ton/upto/types.py +215 -0
- t402/schemes/tron/__init__.py +21 -4
- t402/schemes/tron/upto/__init__.py +30 -0
- t402/schemes/tron/upto/types.py +213 -0
- t402/starlette/__init__.py +38 -0
- t402/starlette/middleware.py +522 -0
- t402/ton.py +1 -1
- t402/ton_paywall_template.py +1 -1
- t402/types.py +100 -2
- t402/wdk/chains.py +1 -1
- {t402-1.9.1.dist-info → t402-1.10.0.dist-info}/METADATA +3 -3
- {t402-1.9.1.dist-info → t402-1.10.0.dist-info}/RECORD +51 -20
- {t402-1.9.1.dist-info → t402-1.10.0.dist-info}/WHEEL +0 -0
- {t402-1.9.1.dist-info → t402-1.10.0.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"""Cosmos/Noble blockchain constants for the T402 protocol.
|
|
2
|
+
|
|
3
|
+
This module contains network configurations, token denominations,
|
|
4
|
+
and other constants used by the Cosmos exact-direct payment scheme.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Dict, Optional
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# Scheme identifier
|
|
13
|
+
SCHEME_EXACT_DIRECT = "exact-direct"
|
|
14
|
+
|
|
15
|
+
# CAIP-2 network identifiers
|
|
16
|
+
# Noble is a Cosmos app-chain specifically built for native USDC issuance
|
|
17
|
+
COSMOS_NOBLE_MAINNET = "cosmos:noble-1"
|
|
18
|
+
COSMOS_NOBLE_TESTNET = "cosmos:grand-1"
|
|
19
|
+
|
|
20
|
+
# RPC endpoints
|
|
21
|
+
NOBLE_MAINNET_RPC = "https://noble-rpc.polkachu.com"
|
|
22
|
+
NOBLE_TESTNET_RPC = "https://rpc.testnet.noble.strange.love"
|
|
23
|
+
|
|
24
|
+
# REST API endpoints
|
|
25
|
+
NOBLE_MAINNET_REST = "https://noble-api.polkachu.com"
|
|
26
|
+
NOBLE_TESTNET_REST = "https://api.testnet.noble.strange.love"
|
|
27
|
+
|
|
28
|
+
# Address prefix (bech32)
|
|
29
|
+
NOBLE_BECH32_PREFIX = "noble"
|
|
30
|
+
|
|
31
|
+
# USDC denom on Noble (micro USDC: 1 USDC = 1,000,000 uusdc)
|
|
32
|
+
USDC_DENOM = "uusdc"
|
|
33
|
+
|
|
34
|
+
# Gas settings
|
|
35
|
+
DEFAULT_GAS_LIMIT = 200000
|
|
36
|
+
DEFAULT_GAS_PRICE = "0.025uusdc"
|
|
37
|
+
DEFAULT_FEE_AMOUNT = "5000" # 0.005 USDC
|
|
38
|
+
DEFAULT_FEE_DENOM = USDC_DENOM
|
|
39
|
+
|
|
40
|
+
# Message types
|
|
41
|
+
MSG_TYPE_SEND = "/cosmos.bank.v1beta1.MsgSend"
|
|
42
|
+
MSG_TYPE_MULTI_SEND = "/cosmos.bank.v1beta1.MsgMultiSend"
|
|
43
|
+
|
|
44
|
+
# CAIP family pattern
|
|
45
|
+
CAIP_FAMILY = "cosmos:*"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class TokenInfo:
|
|
49
|
+
"""Contains information about a Cosmos token.
|
|
50
|
+
|
|
51
|
+
Attributes:
|
|
52
|
+
denom: The token denomination (e.g., "uusdc").
|
|
53
|
+
symbol: The token symbol (e.g., "USDC").
|
|
54
|
+
decimals: The number of decimal places for the token.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
def __init__(self, denom: str, symbol: str, decimals: int) -> None:
|
|
58
|
+
self.denom = denom
|
|
59
|
+
self.symbol = symbol
|
|
60
|
+
self.decimals = decimals
|
|
61
|
+
|
|
62
|
+
def __repr__(self) -> str:
|
|
63
|
+
return f"TokenInfo(denom={self.denom!r}, symbol={self.symbol!r}, decimals={self.decimals})"
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class NetworkConfig:
|
|
67
|
+
"""Network-specific configuration for Cosmos.
|
|
68
|
+
|
|
69
|
+
Attributes:
|
|
70
|
+
chain_id: The Cosmos chain ID (e.g., "noble-1").
|
|
71
|
+
rpc_url: The RPC endpoint URL.
|
|
72
|
+
rest_url: The REST API endpoint URL.
|
|
73
|
+
bech32_prefix: The bech32 address prefix (e.g., "noble").
|
|
74
|
+
default_token: The default token for this network.
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
def __init__(
|
|
78
|
+
self,
|
|
79
|
+
chain_id: str,
|
|
80
|
+
rpc_url: str,
|
|
81
|
+
rest_url: str,
|
|
82
|
+
bech32_prefix: str,
|
|
83
|
+
default_token: TokenInfo,
|
|
84
|
+
) -> None:
|
|
85
|
+
self.chain_id = chain_id
|
|
86
|
+
self.rpc_url = rpc_url
|
|
87
|
+
self.rest_url = rest_url
|
|
88
|
+
self.bech32_prefix = bech32_prefix
|
|
89
|
+
self.default_token = default_token
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
# Token definitions
|
|
93
|
+
USDC_TOKEN = TokenInfo(
|
|
94
|
+
denom=USDC_DENOM,
|
|
95
|
+
symbol="USDC",
|
|
96
|
+
decimals=6,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# Network configurations
|
|
100
|
+
NETWORK_CONFIGS: Dict[str, NetworkConfig] = {
|
|
101
|
+
COSMOS_NOBLE_MAINNET: NetworkConfig(
|
|
102
|
+
chain_id="noble-1",
|
|
103
|
+
rpc_url=NOBLE_MAINNET_RPC,
|
|
104
|
+
rest_url=NOBLE_MAINNET_REST,
|
|
105
|
+
bech32_prefix=NOBLE_BECH32_PREFIX,
|
|
106
|
+
default_token=USDC_TOKEN,
|
|
107
|
+
),
|
|
108
|
+
COSMOS_NOBLE_TESTNET: NetworkConfig(
|
|
109
|
+
chain_id="grand-1",
|
|
110
|
+
rpc_url=NOBLE_TESTNET_RPC,
|
|
111
|
+
rest_url=NOBLE_TESTNET_REST,
|
|
112
|
+
bech32_prefix=NOBLE_BECH32_PREFIX,
|
|
113
|
+
default_token=USDC_TOKEN,
|
|
114
|
+
),
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
# Token registry: network -> symbol -> TokenInfo
|
|
118
|
+
TOKEN_REGISTRY: Dict[str, Dict[str, TokenInfo]] = {
|
|
119
|
+
COSMOS_NOBLE_MAINNET: {
|
|
120
|
+
"USDC": USDC_TOKEN,
|
|
121
|
+
},
|
|
122
|
+
COSMOS_NOBLE_TESTNET: {
|
|
123
|
+
"USDC": USDC_TOKEN,
|
|
124
|
+
},
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def get_network_config(network: str) -> Optional[NetworkConfig]:
|
|
129
|
+
"""Get the configuration for a Cosmos network.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
network: The CAIP-2 network identifier (e.g., "cosmos:noble-1").
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
NetworkConfig if the network is supported, None otherwise.
|
|
136
|
+
"""
|
|
137
|
+
return NETWORK_CONFIGS.get(network)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def is_valid_network(network: str) -> bool:
|
|
141
|
+
"""Check if a network identifier is a supported Cosmos network.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
network: The CAIP-2 network identifier.
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
True if the network is supported.
|
|
148
|
+
"""
|
|
149
|
+
return network in NETWORK_CONFIGS
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def get_token_info(network: str, symbol: str) -> Optional[TokenInfo]:
|
|
153
|
+
"""Get token info for a network and symbol.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
network: The CAIP-2 network identifier.
|
|
157
|
+
symbol: The token symbol (e.g., "USDC").
|
|
158
|
+
|
|
159
|
+
Returns:
|
|
160
|
+
TokenInfo if found, None otherwise.
|
|
161
|
+
"""
|
|
162
|
+
tokens = TOKEN_REGISTRY.get(network)
|
|
163
|
+
if tokens is None:
|
|
164
|
+
return None
|
|
165
|
+
return tokens.get(symbol)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def get_token_by_denom(network: str, denom: str) -> Optional[TokenInfo]:
|
|
169
|
+
"""Get token info by denomination.
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
network: The CAIP-2 network identifier.
|
|
173
|
+
denom: The token denomination (e.g., "uusdc").
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
TokenInfo if found, None otherwise.
|
|
177
|
+
"""
|
|
178
|
+
tokens = TOKEN_REGISTRY.get(network)
|
|
179
|
+
if tokens is None:
|
|
180
|
+
return None
|
|
181
|
+
for token in tokens.values():
|
|
182
|
+
if token.denom == denom:
|
|
183
|
+
return token
|
|
184
|
+
return None
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def is_valid_address(address: str, expected_prefix: str) -> bool:
|
|
188
|
+
"""Check if an address has the correct bech32 prefix.
|
|
189
|
+
|
|
190
|
+
Validates that the address starts with the expected prefix followed by "1"
|
|
191
|
+
(the bech32 separator) and has sufficient length.
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
address: The bech32 address to validate.
|
|
195
|
+
expected_prefix: The expected prefix (e.g., "noble").
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
True if the address has the correct prefix format.
|
|
199
|
+
"""
|
|
200
|
+
if not address or len(address) < len(expected_prefix) + 1:
|
|
201
|
+
return False
|
|
202
|
+
return address[:len(expected_prefix)] == expected_prefix
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def get_supported_networks() -> list:
|
|
206
|
+
"""Get a list of supported Cosmos network identifiers.
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
List of CAIP-2 network identifier strings.
|
|
210
|
+
"""
|
|
211
|
+
return [COSMOS_NOBLE_MAINNET, COSMOS_NOBLE_TESTNET]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Cosmos/Noble Exact-Direct Payment Scheme.
|
|
2
|
+
|
|
3
|
+
This package provides the exact-direct payment scheme implementation for Cosmos/Noble.
|
|
4
|
+
In this scheme, the client executes a bank MsgSend on-chain directly,
|
|
5
|
+
and the transaction hash is used as proof of payment.
|
|
6
|
+
|
|
7
|
+
Components:
|
|
8
|
+
- ExactDirectCosmosClientScheme: Client-side (executes transfer, returns tx hash)
|
|
9
|
+
- ExactDirectCosmosServerScheme: Server-side (parses prices, enhances requirements)
|
|
10
|
+
- ExactDirectCosmosFacilitatorScheme: Facilitator-side (verifies tx, marks settled)
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from t402.schemes.cosmos.exact_direct.client import ExactDirectCosmosClientScheme
|
|
14
|
+
from t402.schemes.cosmos.exact_direct.server import ExactDirectCosmosServerScheme
|
|
15
|
+
from t402.schemes.cosmos.exact_direct.facilitator import ExactDirectCosmosFacilitatorScheme
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"ExactDirectCosmosClientScheme",
|
|
19
|
+
"ExactDirectCosmosServerScheme",
|
|
20
|
+
"ExactDirectCosmosFacilitatorScheme",
|
|
21
|
+
]
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
"""Cosmos/Noble Exact-Direct Scheme - Client Implementation.
|
|
2
|
+
|
|
3
|
+
This module provides the client-side implementation of the exact-direct payment
|
|
4
|
+
scheme for Cosmos/Noble networks using bank MsgSend.
|
|
5
|
+
|
|
6
|
+
Unlike other schemes where the client creates a signed message for the facilitator
|
|
7
|
+
to execute, the exact-direct scheme has the client execute the transfer directly.
|
|
8
|
+
The transaction hash is then used as proof of payment.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import logging
|
|
14
|
+
from typing import Any, Dict, Optional, Union
|
|
15
|
+
|
|
16
|
+
from t402.types import PaymentRequirementsV2
|
|
17
|
+
from t402.schemes.cosmos.constants import (
|
|
18
|
+
SCHEME_EXACT_DIRECT,
|
|
19
|
+
CAIP_FAMILY,
|
|
20
|
+
USDC_DENOM,
|
|
21
|
+
is_valid_network,
|
|
22
|
+
is_valid_address,
|
|
23
|
+
get_network_config,
|
|
24
|
+
get_token_info,
|
|
25
|
+
)
|
|
26
|
+
from t402.schemes.cosmos.types import (
|
|
27
|
+
ClientCosmosSigner,
|
|
28
|
+
ExactDirectPayload,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
logger = logging.getLogger(__name__)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class ExactDirectCosmosClientConfig:
|
|
36
|
+
"""Configuration for the ExactDirectCosmosClientScheme.
|
|
37
|
+
|
|
38
|
+
Attributes:
|
|
39
|
+
memo: Optional memo to include in the transaction.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def __init__(
|
|
43
|
+
self,
|
|
44
|
+
memo: Optional[str] = None,
|
|
45
|
+
) -> None:
|
|
46
|
+
self.memo = memo
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class ExactDirectCosmosClientScheme:
|
|
50
|
+
"""Client scheme for Cosmos/Noble exact-direct payments using bank MsgSend.
|
|
51
|
+
|
|
52
|
+
This scheme executes the token transfer on-chain directly and returns the
|
|
53
|
+
transaction hash as proof of payment. The facilitator then verifies the
|
|
54
|
+
transaction was successful.
|
|
55
|
+
|
|
56
|
+
Example:
|
|
57
|
+
```python
|
|
58
|
+
class MyCosmosSigner:
|
|
59
|
+
def address(self) -> str:
|
|
60
|
+
return "noble1abc..."
|
|
61
|
+
|
|
62
|
+
async def send_tokens(
|
|
63
|
+
self, network, to, amount, denom
|
|
64
|
+
) -> str:
|
|
65
|
+
# Execute the transaction
|
|
66
|
+
return "AABB1122..."
|
|
67
|
+
|
|
68
|
+
signer = MyCosmosSigner()
|
|
69
|
+
scheme = ExactDirectCosmosClientScheme(signer)
|
|
70
|
+
|
|
71
|
+
payload = await scheme.create_payment_payload(
|
|
72
|
+
t402_version=2,
|
|
73
|
+
requirements=requirements,
|
|
74
|
+
)
|
|
75
|
+
```
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
def __init__(
|
|
79
|
+
self,
|
|
80
|
+
signer: ClientCosmosSigner,
|
|
81
|
+
config: Optional[ExactDirectCosmosClientConfig] = None,
|
|
82
|
+
) -> None:
|
|
83
|
+
"""Initialize with a Cosmos signer.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
signer: Any object implementing the ClientCosmosSigner protocol.
|
|
87
|
+
config: Optional configuration for the client scheme.
|
|
88
|
+
"""
|
|
89
|
+
self._signer = signer
|
|
90
|
+
self._config = config or ExactDirectCosmosClientConfig()
|
|
91
|
+
|
|
92
|
+
@property
|
|
93
|
+
def scheme(self) -> str:
|
|
94
|
+
"""The scheme identifier."""
|
|
95
|
+
return SCHEME_EXACT_DIRECT
|
|
96
|
+
|
|
97
|
+
@property
|
|
98
|
+
def caip_family(self) -> str:
|
|
99
|
+
"""The CAIP-2 family pattern for Cosmos networks."""
|
|
100
|
+
return CAIP_FAMILY
|
|
101
|
+
|
|
102
|
+
@property
|
|
103
|
+
def signer_address(self) -> str:
|
|
104
|
+
"""Get the signer's Cosmos address."""
|
|
105
|
+
return self._signer.address()
|
|
106
|
+
|
|
107
|
+
async def create_payment_payload(
|
|
108
|
+
self,
|
|
109
|
+
t402_version: int,
|
|
110
|
+
requirements: Union[PaymentRequirementsV2, Dict[str, Any]],
|
|
111
|
+
) -> Dict[str, Any]:
|
|
112
|
+
"""Create a payment payload by executing a bank send on-chain.
|
|
113
|
+
|
|
114
|
+
Executes a Cosmos bank MsgSend to the specified recipient and returns
|
|
115
|
+
the transaction hash as proof of payment.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
t402_version: The T402 protocol version.
|
|
119
|
+
requirements: Payment requirements with amount, asset, payTo, network.
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
Dict with t402Version and payload containing txHash, from, to, amount.
|
|
123
|
+
|
|
124
|
+
Raises:
|
|
125
|
+
ValueError: If requirements are invalid (bad network, missing fields,
|
|
126
|
+
invalid addresses).
|
|
127
|
+
RuntimeError: If the transaction execution fails.
|
|
128
|
+
"""
|
|
129
|
+
# Extract requirements as dict
|
|
130
|
+
if hasattr(requirements, "model_dump"):
|
|
131
|
+
req = requirements.model_dump(by_alias=True)
|
|
132
|
+
else:
|
|
133
|
+
req = dict(requirements)
|
|
134
|
+
|
|
135
|
+
network = req.get("network", "")
|
|
136
|
+
asset = req.get("asset", "")
|
|
137
|
+
pay_to = req.get("payTo") or req.get("pay_to", "")
|
|
138
|
+
amount = req.get("amount", "")
|
|
139
|
+
|
|
140
|
+
# Validate network
|
|
141
|
+
if not is_valid_network(network):
|
|
142
|
+
raise ValueError(f"Unsupported network: {network}")
|
|
143
|
+
|
|
144
|
+
# Get network config for address validation
|
|
145
|
+
config = get_network_config(network)
|
|
146
|
+
if config is None:
|
|
147
|
+
raise ValueError(f"Unsupported network: {network}")
|
|
148
|
+
|
|
149
|
+
# Validate required fields
|
|
150
|
+
if not pay_to:
|
|
151
|
+
raise ValueError("payTo address is required")
|
|
152
|
+
if not amount:
|
|
153
|
+
raise ValueError("Amount is required")
|
|
154
|
+
|
|
155
|
+
# Determine denom
|
|
156
|
+
denom = USDC_DENOM
|
|
157
|
+
if asset:
|
|
158
|
+
# Check if asset is a symbol or denom
|
|
159
|
+
token = get_token_info(network, asset)
|
|
160
|
+
if token:
|
|
161
|
+
denom = token.denom
|
|
162
|
+
else:
|
|
163
|
+
# Assume it's already a denom
|
|
164
|
+
denom = asset
|
|
165
|
+
|
|
166
|
+
# Validate recipient address
|
|
167
|
+
if not is_valid_address(pay_to, config.bech32_prefix):
|
|
168
|
+
raise ValueError(f"Invalid recipient address: {pay_to}")
|
|
169
|
+
|
|
170
|
+
# Validate sender address
|
|
171
|
+
sender = self._signer.address()
|
|
172
|
+
if not is_valid_address(sender, config.bech32_prefix):
|
|
173
|
+
raise ValueError(f"Invalid sender address: {sender}")
|
|
174
|
+
|
|
175
|
+
# Execute the transfer via the signer
|
|
176
|
+
try:
|
|
177
|
+
tx_hash = await self._signer.send_tokens(
|
|
178
|
+
network=network,
|
|
179
|
+
to=pay_to,
|
|
180
|
+
amount=amount,
|
|
181
|
+
denom=denom,
|
|
182
|
+
)
|
|
183
|
+
except Exception as e:
|
|
184
|
+
raise RuntimeError(f"Failed to send tokens: {e}") from e
|
|
185
|
+
|
|
186
|
+
# Build the payload
|
|
187
|
+
payload = ExactDirectPayload(
|
|
188
|
+
tx_hash=tx_hash,
|
|
189
|
+
from_address=sender,
|
|
190
|
+
to_address=pay_to,
|
|
191
|
+
amount=amount,
|
|
192
|
+
denom=denom,
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
"t402Version": t402_version,
|
|
197
|
+
"payload": payload.to_map(),
|
|
198
|
+
}
|