t402 1.7.1__py3-none-any.whl → 1.9.1__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 +2 -1
- t402/bridge/client.py +13 -5
- t402/bridge/constants.py +3 -1
- t402/bridge/router.py +1 -1
- t402/bridge/scan.py +3 -1
- t402/chains.py +268 -1
- t402/cli.py +31 -9
- t402/common.py +2 -0
- t402/cosmos_paywall_template.py +2 -0
- t402/encoding.py +9 -3
- t402/erc4337/accounts.py +56 -51
- t402/erc4337/bundlers.py +105 -99
- t402/erc4337/paymasters.py +100 -109
- t402/erc4337/types.py +39 -26
- t402/evm_paywall_template.py +1 -1
- t402/fastapi/middleware.py +1 -3
- t402/mcp/server.py +79 -46
- t402/near_paywall_template.py +2 -0
- t402/networks.py +34 -1
- t402/paywall.py +1 -3
- t402/schemes/__init__.py +164 -1
- t402/schemes/aptos/__init__.py +70 -0
- t402/schemes/aptos/constants.py +349 -0
- t402/schemes/aptos/exact_direct/__init__.py +44 -0
- t402/schemes/aptos/exact_direct/client.py +202 -0
- t402/schemes/aptos/exact_direct/facilitator.py +426 -0
- t402/schemes/aptos/exact_direct/server.py +272 -0
- t402/schemes/aptos/types.py +237 -0
- t402/schemes/evm/__init__.py +67 -1
- t402/schemes/evm/exact/__init__.py +11 -0
- t402/schemes/evm/exact/client.py +3 -1
- t402/schemes/evm/exact/facilitator.py +894 -0
- t402/schemes/evm/exact/server.py +1 -1
- t402/schemes/evm/exact_legacy/__init__.py +38 -0
- t402/schemes/evm/exact_legacy/client.py +291 -0
- t402/schemes/evm/exact_legacy/facilitator.py +777 -0
- t402/schemes/evm/exact_legacy/server.py +231 -0
- t402/schemes/evm/upto/__init__.py +70 -0
- t402/schemes/evm/upto/client.py +244 -0
- t402/schemes/evm/upto/facilitator.py +625 -0
- t402/schemes/evm/upto/server.py +243 -0
- t402/schemes/evm/upto/types.py +307 -0
- t402/schemes/interfaces.py +6 -2
- t402/schemes/near/__init__.py +112 -0
- t402/schemes/near/constants.py +189 -0
- t402/schemes/near/exact_direct/__init__.py +21 -0
- t402/schemes/near/exact_direct/client.py +204 -0
- t402/schemes/near/exact_direct/facilitator.py +455 -0
- t402/schemes/near/exact_direct/server.py +303 -0
- t402/schemes/near/types.py +419 -0
- t402/schemes/polkadot/__init__.py +72 -0
- t402/schemes/polkadot/constants.py +155 -0
- t402/schemes/polkadot/exact_direct/__init__.py +43 -0
- t402/schemes/polkadot/exact_direct/client.py +235 -0
- t402/schemes/polkadot/exact_direct/facilitator.py +428 -0
- t402/schemes/polkadot/exact_direct/server.py +292 -0
- t402/schemes/polkadot/types.py +385 -0
- t402/schemes/registry.py +6 -2
- t402/schemes/stacks/__init__.py +68 -0
- t402/schemes/stacks/constants.py +122 -0
- t402/schemes/stacks/exact_direct/__init__.py +43 -0
- t402/schemes/stacks/exact_direct/client.py +222 -0
- t402/schemes/stacks/exact_direct/facilitator.py +424 -0
- t402/schemes/stacks/exact_direct/server.py +292 -0
- t402/schemes/stacks/types.py +380 -0
- t402/schemes/svm/__init__.py +29 -0
- t402/schemes/svm/exact/__init__.py +35 -0
- t402/schemes/svm/exact/client.py +23 -0
- t402/schemes/svm/exact/facilitator.py +24 -0
- t402/schemes/svm/exact/server.py +20 -0
- t402/schemes/tezos/__init__.py +84 -0
- t402/schemes/tezos/constants.py +372 -0
- t402/schemes/tezos/exact_direct/__init__.py +22 -0
- t402/schemes/tezos/exact_direct/client.py +226 -0
- t402/schemes/tezos/exact_direct/facilitator.py +491 -0
- t402/schemes/tezos/exact_direct/server.py +277 -0
- t402/schemes/tezos/types.py +220 -0
- t402/schemes/ton/__init__.py +9 -2
- t402/schemes/ton/exact/__init__.py +7 -0
- t402/schemes/ton/exact/facilitator.py +730 -0
- t402/schemes/ton/exact/server.py +1 -1
- t402/schemes/tron/__init__.py +11 -2
- t402/schemes/tron/exact/__init__.py +9 -0
- t402/schemes/tron/exact/facilitator.py +673 -0
- t402/schemes/tron/exact/server.py +1 -1
- t402/schemes/upto/__init__.py +80 -0
- t402/schemes/upto/types.py +376 -0
- t402/stacks_paywall_template.py +2 -0
- t402/svm.py +45 -11
- t402/svm_paywall_template.py +1 -1
- t402/ton.py +5 -1
- t402/ton_paywall_template.py +1 -192
- t402/tron.py +2 -0
- t402/tron_paywall_template.py +2 -0
- t402/types.py +4 -2
- t402/wdk/errors.py +15 -5
- t402/wdk/signer.py +11 -2
- {t402-1.7.1.dist-info → t402-1.9.1.dist-info}/METADATA +42 -1
- t402-1.9.1.dist-info/RECORD +125 -0
- t402-1.7.1.dist-info/RECORD +0 -67
- {t402-1.7.1.dist-info → t402-1.9.1.dist-info}/WHEEL +0 -0
- {t402-1.7.1.dist-info → t402-1.9.1.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"""NEAR blockchain constants for the T402 protocol.
|
|
2
|
+
|
|
3
|
+
This module contains network configurations, token contract addresses,
|
|
4
|
+
and other constants used by the NEAR 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
|
+
NEAR_MAINNET = "near:mainnet"
|
|
17
|
+
NEAR_TESTNET = "near:testnet"
|
|
18
|
+
|
|
19
|
+
# RPC endpoints
|
|
20
|
+
NEAR_MAINNET_RPC = "https://rpc.mainnet.near.org"
|
|
21
|
+
NEAR_TESTNET_RPC = "https://rpc.testnet.near.org"
|
|
22
|
+
|
|
23
|
+
# Default gas for ft_transfer (30 TGas)
|
|
24
|
+
DEFAULT_GAS = "30000000000000"
|
|
25
|
+
DEFAULT_GAS_INT = 30_000_000_000_000
|
|
26
|
+
|
|
27
|
+
# Storage deposit required (1 yoctoNEAR) for ft_transfer
|
|
28
|
+
STORAGE_DEPOSIT = "1"
|
|
29
|
+
|
|
30
|
+
# NEP-141 function names
|
|
31
|
+
FUNCTION_FT_TRANSFER = "ft_transfer"
|
|
32
|
+
FUNCTION_FT_BALANCE_OF = "ft_balance_of"
|
|
33
|
+
FUNCTION_STORAGE_BALANCE = "storage_balance_of"
|
|
34
|
+
|
|
35
|
+
# CAIP family pattern
|
|
36
|
+
CAIP_FAMILY = "near:*"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class TokenInfo:
|
|
40
|
+
"""Contains information about a NEAR fungible token.
|
|
41
|
+
|
|
42
|
+
Attributes:
|
|
43
|
+
contract_id: The NEAR account ID of the token contract.
|
|
44
|
+
symbol: The token symbol (e.g., "USDT").
|
|
45
|
+
decimals: The number of decimal places for the token.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
def __init__(self, contract_id: str, symbol: str, decimals: int) -> None:
|
|
49
|
+
self.contract_id = contract_id
|
|
50
|
+
self.symbol = symbol
|
|
51
|
+
self.decimals = decimals
|
|
52
|
+
|
|
53
|
+
def __repr__(self) -> str:
|
|
54
|
+
return f"TokenInfo(contract_id={self.contract_id!r}, symbol={self.symbol!r}, decimals={self.decimals})"
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class NetworkConfig:
|
|
58
|
+
"""Network-specific configuration for NEAR.
|
|
59
|
+
|
|
60
|
+
Attributes:
|
|
61
|
+
network_id: The short network identifier (e.g., "mainnet").
|
|
62
|
+
rpc_url: The RPC endpoint URL.
|
|
63
|
+
default_token: The default token for this network.
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
def __init__(self, network_id: str, rpc_url: str, default_token: TokenInfo) -> None:
|
|
67
|
+
self.network_id = network_id
|
|
68
|
+
self.rpc_url = rpc_url
|
|
69
|
+
self.default_token = default_token
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
# Token definitions
|
|
73
|
+
USDT_MAINNET = TokenInfo(
|
|
74
|
+
contract_id="usdt.tether-token.near",
|
|
75
|
+
symbol="USDT",
|
|
76
|
+
decimals=6,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
USDT_TESTNET = TokenInfo(
|
|
80
|
+
contract_id="usdt.fakes.testnet",
|
|
81
|
+
symbol="USDT",
|
|
82
|
+
decimals=6,
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
USDC_MAINNET = TokenInfo(
|
|
86
|
+
contract_id="17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1",
|
|
87
|
+
symbol="USDC",
|
|
88
|
+
decimals=6,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
USDC_TESTNET = TokenInfo(
|
|
92
|
+
contract_id="usdc.fakes.testnet",
|
|
93
|
+
symbol="USDC",
|
|
94
|
+
decimals=6,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
# Network configurations
|
|
98
|
+
NETWORK_CONFIGS: Dict[str, NetworkConfig] = {
|
|
99
|
+
NEAR_MAINNET: NetworkConfig(
|
|
100
|
+
network_id="mainnet",
|
|
101
|
+
rpc_url=NEAR_MAINNET_RPC,
|
|
102
|
+
default_token=USDT_MAINNET,
|
|
103
|
+
),
|
|
104
|
+
NEAR_TESTNET: NetworkConfig(
|
|
105
|
+
network_id="testnet",
|
|
106
|
+
rpc_url=NEAR_TESTNET_RPC,
|
|
107
|
+
default_token=USDT_TESTNET,
|
|
108
|
+
),
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
# Token registry: network -> symbol -> TokenInfo
|
|
112
|
+
TOKEN_REGISTRY: Dict[str, Dict[str, TokenInfo]] = {
|
|
113
|
+
NEAR_MAINNET: {
|
|
114
|
+
"USDT": USDT_MAINNET,
|
|
115
|
+
"USDC": USDC_MAINNET,
|
|
116
|
+
},
|
|
117
|
+
NEAR_TESTNET: {
|
|
118
|
+
"USDT": USDT_TESTNET,
|
|
119
|
+
"USDC": USDC_TESTNET,
|
|
120
|
+
},
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def get_network_config(network: str) -> Optional[NetworkConfig]:
|
|
125
|
+
"""Get the configuration for a NEAR network.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
network: The CAIP-2 network identifier (e.g., "near:mainnet").
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
NetworkConfig if the network is supported, None otherwise.
|
|
132
|
+
"""
|
|
133
|
+
return NETWORK_CONFIGS.get(network)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def is_valid_network(network: str) -> bool:
|
|
137
|
+
"""Check if a network identifier is a supported NEAR network.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
network: The CAIP-2 network identifier.
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
True if the network is supported.
|
|
144
|
+
"""
|
|
145
|
+
return network in NETWORK_CONFIGS
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def get_token_info(network: str, symbol: str) -> Optional[TokenInfo]:
|
|
149
|
+
"""Get token info for a network and symbol.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
network: The CAIP-2 network identifier.
|
|
153
|
+
symbol: The token symbol (e.g., "USDT").
|
|
154
|
+
|
|
155
|
+
Returns:
|
|
156
|
+
TokenInfo if found, None otherwise.
|
|
157
|
+
"""
|
|
158
|
+
tokens = TOKEN_REGISTRY.get(network)
|
|
159
|
+
if tokens is None:
|
|
160
|
+
return None
|
|
161
|
+
return tokens.get(symbol)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def get_token_by_contract(network: str, contract_id: str) -> Optional[TokenInfo]:
|
|
165
|
+
"""Get token info by contract address.
|
|
166
|
+
|
|
167
|
+
Args:
|
|
168
|
+
network: The CAIP-2 network identifier.
|
|
169
|
+
contract_id: The token contract account ID.
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
TokenInfo if found, None otherwise.
|
|
173
|
+
"""
|
|
174
|
+
tokens = TOKEN_REGISTRY.get(network)
|
|
175
|
+
if tokens is None:
|
|
176
|
+
return None
|
|
177
|
+
for token in tokens.values():
|
|
178
|
+
if token.contract_id == contract_id:
|
|
179
|
+
return token
|
|
180
|
+
return None
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def get_supported_networks() -> list:
|
|
184
|
+
"""Get a list of supported NEAR network identifiers.
|
|
185
|
+
|
|
186
|
+
Returns:
|
|
187
|
+
List of CAIP-2 network identifier strings.
|
|
188
|
+
"""
|
|
189
|
+
return [NEAR_MAINNET, NEAR_TESTNET]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""NEAR Exact-Direct Payment Scheme.
|
|
2
|
+
|
|
3
|
+
This package provides the exact-direct payment scheme implementation for NEAR.
|
|
4
|
+
In this scheme, the client executes the NEP-141 ft_transfer on-chain directly,
|
|
5
|
+
and the transaction hash is used as proof of payment.
|
|
6
|
+
|
|
7
|
+
Components:
|
|
8
|
+
- ExactDirectNearClientScheme: Client-side (executes transfer, returns tx hash)
|
|
9
|
+
- ExactDirectNearServerScheme: Server-side (parses prices, enhances requirements)
|
|
10
|
+
- ExactDirectNearFacilitatorScheme: Facilitator-side (verifies tx, marks settled)
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from t402.schemes.near.exact_direct.client import ExactDirectNearClientScheme
|
|
14
|
+
from t402.schemes.near.exact_direct.server import ExactDirectNearServerScheme
|
|
15
|
+
from t402.schemes.near.exact_direct.facilitator import ExactDirectNearFacilitatorScheme
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"ExactDirectNearClientScheme",
|
|
19
|
+
"ExactDirectNearServerScheme",
|
|
20
|
+
"ExactDirectNearFacilitatorScheme",
|
|
21
|
+
]
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"""NEAR Exact-Direct Scheme - Client Implementation.
|
|
2
|
+
|
|
3
|
+
This module provides the client-side implementation of the exact-direct payment
|
|
4
|
+
scheme for NEAR networks using NEP-141 ft_transfer.
|
|
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 json
|
|
14
|
+
import logging
|
|
15
|
+
from typing import Any, Dict, List, Optional, Union
|
|
16
|
+
|
|
17
|
+
from t402.types import PaymentRequirementsV2
|
|
18
|
+
from t402.schemes.near.constants import (
|
|
19
|
+
SCHEME_EXACT_DIRECT,
|
|
20
|
+
CAIP_FAMILY,
|
|
21
|
+
DEFAULT_GAS_INT,
|
|
22
|
+
STORAGE_DEPOSIT,
|
|
23
|
+
FUNCTION_FT_TRANSFER,
|
|
24
|
+
is_valid_network,
|
|
25
|
+
)
|
|
26
|
+
from t402.schemes.near.types import (
|
|
27
|
+
ClientNearSigner,
|
|
28
|
+
ExactDirectPayload,
|
|
29
|
+
is_valid_account_id,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
logger = logging.getLogger(__name__)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class ExactDirectNearClientConfig:
|
|
37
|
+
"""Configuration for the ExactDirectNearClientScheme.
|
|
38
|
+
|
|
39
|
+
Attributes:
|
|
40
|
+
memo: Optional memo to include in the ft_transfer call.
|
|
41
|
+
gas_amount: Gas to attach to the ft_transfer call (default: 30 TGas).
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
def __init__(
|
|
45
|
+
self,
|
|
46
|
+
memo: Optional[str] = None,
|
|
47
|
+
gas_amount: Optional[int] = None,
|
|
48
|
+
) -> None:
|
|
49
|
+
self.memo = memo
|
|
50
|
+
self.gas_amount = gas_amount or DEFAULT_GAS_INT
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class ExactDirectNearClientScheme:
|
|
54
|
+
"""Client scheme for NEAR exact-direct payments using NEP-141 ft_transfer.
|
|
55
|
+
|
|
56
|
+
This scheme executes the token transfer on-chain directly and returns the
|
|
57
|
+
transaction hash as proof of payment. The facilitator then verifies the
|
|
58
|
+
transaction was successful.
|
|
59
|
+
|
|
60
|
+
Example:
|
|
61
|
+
```python
|
|
62
|
+
class MyNearSigner:
|
|
63
|
+
def account_id(self) -> str:
|
|
64
|
+
return "alice.near"
|
|
65
|
+
|
|
66
|
+
async def sign_and_send_transaction(
|
|
67
|
+
self, receiver_id, actions, network
|
|
68
|
+
) -> str:
|
|
69
|
+
# Execute the transaction
|
|
70
|
+
return "Abc123TxHash..."
|
|
71
|
+
|
|
72
|
+
signer = MyNearSigner()
|
|
73
|
+
scheme = ExactDirectNearClientScheme(signer)
|
|
74
|
+
|
|
75
|
+
payload = await scheme.create_payment_payload(
|
|
76
|
+
t402_version=2,
|
|
77
|
+
requirements=requirements,
|
|
78
|
+
)
|
|
79
|
+
```
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
def __init__(
|
|
83
|
+
self,
|
|
84
|
+
signer: ClientNearSigner,
|
|
85
|
+
config: Optional[ExactDirectNearClientConfig] = None,
|
|
86
|
+
) -> None:
|
|
87
|
+
"""Initialize with a NEAR signer.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
signer: Any object implementing the ClientNearSigner protocol.
|
|
91
|
+
config: Optional configuration for the client scheme.
|
|
92
|
+
"""
|
|
93
|
+
self._signer = signer
|
|
94
|
+
self._config = config or ExactDirectNearClientConfig()
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def scheme(self) -> str:
|
|
98
|
+
"""The scheme identifier."""
|
|
99
|
+
return SCHEME_EXACT_DIRECT
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def caip_family(self) -> str:
|
|
103
|
+
"""The CAIP-2 family pattern for NEAR networks."""
|
|
104
|
+
return CAIP_FAMILY
|
|
105
|
+
|
|
106
|
+
@property
|
|
107
|
+
def account_id(self) -> str:
|
|
108
|
+
"""Get the signer's NEAR account ID."""
|
|
109
|
+
return self._signer.account_id()
|
|
110
|
+
|
|
111
|
+
async def create_payment_payload(
|
|
112
|
+
self,
|
|
113
|
+
t402_version: int,
|
|
114
|
+
requirements: Union[PaymentRequirementsV2, Dict[str, Any]],
|
|
115
|
+
) -> Dict[str, Any]:
|
|
116
|
+
"""Create a payment payload by executing ft_transfer on-chain.
|
|
117
|
+
|
|
118
|
+
Executes a NEP-141 ft_transfer to the specified recipient and returns
|
|
119
|
+
the transaction hash as proof of payment.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
t402_version: The T402 protocol version.
|
|
123
|
+
requirements: Payment requirements with amount, asset, payTo, network.
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
Dict with t402Version and payload containing txHash, from, to, amount.
|
|
127
|
+
|
|
128
|
+
Raises:
|
|
129
|
+
ValueError: If requirements are invalid (bad network, missing fields,
|
|
130
|
+
invalid account IDs).
|
|
131
|
+
RuntimeError: If the transaction execution fails.
|
|
132
|
+
"""
|
|
133
|
+
# Extract requirements as dict
|
|
134
|
+
if hasattr(requirements, "model_dump"):
|
|
135
|
+
req = requirements.model_dump(by_alias=True)
|
|
136
|
+
else:
|
|
137
|
+
req = dict(requirements)
|
|
138
|
+
|
|
139
|
+
network = req.get("network", "")
|
|
140
|
+
asset = req.get("asset", "")
|
|
141
|
+
pay_to = req.get("payTo") or req.get("pay_to", "")
|
|
142
|
+
amount = req.get("amount", "")
|
|
143
|
+
|
|
144
|
+
# Validate network
|
|
145
|
+
if not is_valid_network(network):
|
|
146
|
+
raise ValueError(f"Unsupported network: {network}")
|
|
147
|
+
|
|
148
|
+
# Validate required fields
|
|
149
|
+
if not asset:
|
|
150
|
+
raise ValueError("Asset (token contract address) is required")
|
|
151
|
+
if not pay_to:
|
|
152
|
+
raise ValueError("payTo address is required")
|
|
153
|
+
if not amount:
|
|
154
|
+
raise ValueError("Amount is required")
|
|
155
|
+
|
|
156
|
+
# Validate account IDs
|
|
157
|
+
if not is_valid_account_id(pay_to):
|
|
158
|
+
raise ValueError(f"Invalid recipient account ID: {pay_to}")
|
|
159
|
+
sender_id = self._signer.account_id()
|
|
160
|
+
if not is_valid_account_id(sender_id):
|
|
161
|
+
raise ValueError(f"Invalid sender account ID: {sender_id}")
|
|
162
|
+
|
|
163
|
+
# Build ft_transfer arguments
|
|
164
|
+
ft_transfer_args: Dict[str, Any] = {
|
|
165
|
+
"receiver_id": pay_to,
|
|
166
|
+
"amount": amount,
|
|
167
|
+
}
|
|
168
|
+
if self._config.memo:
|
|
169
|
+
ft_transfer_args["memo"] = self._config.memo
|
|
170
|
+
|
|
171
|
+
# Build the function call action
|
|
172
|
+
actions: List[Dict[str, Any]] = [
|
|
173
|
+
{
|
|
174
|
+
"FunctionCall": {
|
|
175
|
+
"method_name": FUNCTION_FT_TRANSFER,
|
|
176
|
+
"args": json.dumps(ft_transfer_args),
|
|
177
|
+
"gas": self._config.gas_amount,
|
|
178
|
+
"deposit": STORAGE_DEPOSIT,
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
]
|
|
182
|
+
|
|
183
|
+
# Execute the transfer via the signer
|
|
184
|
+
try:
|
|
185
|
+
tx_hash = await self._signer.sign_and_send_transaction(
|
|
186
|
+
receiver_id=asset,
|
|
187
|
+
actions=actions,
|
|
188
|
+
network=network,
|
|
189
|
+
)
|
|
190
|
+
except Exception as e:
|
|
191
|
+
raise RuntimeError(f"Failed to execute ft_transfer: {e}") from e
|
|
192
|
+
|
|
193
|
+
# Build the payload
|
|
194
|
+
payload = ExactDirectPayload(
|
|
195
|
+
tx_hash=tx_hash,
|
|
196
|
+
from_account=sender_id,
|
|
197
|
+
to_account=pay_to,
|
|
198
|
+
amount=amount,
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
return {
|
|
202
|
+
"t402Version": t402_version,
|
|
203
|
+
"payload": payload.to_map(),
|
|
204
|
+
}
|