swig-developer-sdk 0.6.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.
- swig_developer_sdk/__init__.py +180 -0
- swig_developer_sdk/client.py +36 -0
- swig_developer_sdk/common.py +104 -0
- swig_developer_sdk/core.py +162 -0
- swig_developer_sdk/evm.py +64 -0
- swig_developer_sdk/one_business.py +163 -0
- swig_developer_sdk/passkeys.py +122 -0
- swig_developer_sdk/paymaster.py +106 -0
- swig_developer_sdk/proxy.py +724 -0
- swig_developer_sdk/ramp.py +724 -0
- swig_developer_sdk/signing.py +461 -0
- swig_developer_sdk/transactions.py +277 -0
- swig_developer_sdk/wallets.py +1393 -0
- swig_developer_sdk-0.6.0.dist-info/METADATA +89 -0
- swig_developer_sdk-0.6.0.dist-info/RECORD +16 -0
- swig_developer_sdk-0.6.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,1393 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import base64
|
|
4
|
+
from collections.abc import Mapping, Sequence
|
|
5
|
+
from dataclasses import dataclass, replace
|
|
6
|
+
from typing import Literal, TypeAlias
|
|
7
|
+
from urllib.parse import quote, urlencode
|
|
8
|
+
|
|
9
|
+
from .common import (
|
|
10
|
+
Amount,
|
|
11
|
+
Network,
|
|
12
|
+
WalletAddressInfo,
|
|
13
|
+
WalletAuthority,
|
|
14
|
+
normalize_amount,
|
|
15
|
+
require_network,
|
|
16
|
+
to_proto_network,
|
|
17
|
+
wallet_address_info_from_wire,
|
|
18
|
+
wallet_authority_to_wire,
|
|
19
|
+
)
|
|
20
|
+
from .core import HttpClient
|
|
21
|
+
from .transactions import (
|
|
22
|
+
PreparedTransaction,
|
|
23
|
+
PreparedTransactionsResult,
|
|
24
|
+
normalize_prepared_transaction,
|
|
25
|
+
normalize_prepared_transactions_result,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass(frozen=True, slots=True)
|
|
30
|
+
class WalletReference:
|
|
31
|
+
swig_config_address: str
|
|
32
|
+
wallet_address: str | None = None
|
|
33
|
+
network: Network | None = None
|
|
34
|
+
requester_authority: WalletAuthority | None = None
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass(frozen=True, slots=True)
|
|
38
|
+
class IdpWalletSession:
|
|
39
|
+
config_address: str
|
|
40
|
+
wallet_address: str
|
|
41
|
+
auth_flow: Literal["session", "role"] | None = None
|
|
42
|
+
updated_at: int | None = None
|
|
43
|
+
requester_authority: WalletAuthority | None = None
|
|
44
|
+
authority_public_key: str | None = None
|
|
45
|
+
role_id: int | None = None
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass(frozen=True, slots=True)
|
|
49
|
+
class RecoveryOptions:
|
|
50
|
+
guardian_pubkey: str | None = None
|
|
51
|
+
delay_seconds: int | None = None
|
|
52
|
+
target_role_id: int | None = None
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@dataclass(frozen=True, slots=True)
|
|
56
|
+
class RecoverySetupPlan:
|
|
57
|
+
requester_authority: WalletAuthority
|
|
58
|
+
guardian_pubkey: str
|
|
59
|
+
delay_seconds: int
|
|
60
|
+
target_role_id: int | None = None
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@dataclass(frozen=True, slots=True)
|
|
64
|
+
class CreateWalletResult:
|
|
65
|
+
wallet: WalletAddressInfo
|
|
66
|
+
transactions: tuple[PreparedTransaction, ...]
|
|
67
|
+
client_authority_transactions: tuple[PreparedTransaction, ...]
|
|
68
|
+
operator_signed_transactions: tuple[PreparedTransaction, ...]
|
|
69
|
+
fee_payer_only_transactions: tuple[PreparedTransaction, ...]
|
|
70
|
+
creation_transaction: PreparedTransaction | None = None
|
|
71
|
+
add_authority_transaction: PreparedTransaction | None = None
|
|
72
|
+
configure_recovery_transaction: PreparedTransaction | None = None
|
|
73
|
+
recovery_setup: RecoverySetupPlan | None = None
|
|
74
|
+
network: Network | None = None
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@dataclass(frozen=True, slots=True)
|
|
78
|
+
class PreparedRecoverySetupResult:
|
|
79
|
+
transactions: tuple[PreparedTransaction, ...]
|
|
80
|
+
client_authority_transactions: tuple[PreparedTransaction, ...]
|
|
81
|
+
operator_signed_transactions: tuple[PreparedTransaction, ...]
|
|
82
|
+
fee_payer_only_transactions: tuple[PreparedTransaction, ...]
|
|
83
|
+
wallet: WalletAddressInfo | None = None
|
|
84
|
+
add_authority_transaction: PreparedTransaction | None = None
|
|
85
|
+
configure_recovery_transaction: PreparedTransaction | None = None
|
|
86
|
+
network: Network | None = None
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@dataclass(frozen=True, slots=True)
|
|
90
|
+
class SolanaAccountMeta:
|
|
91
|
+
pubkey: str
|
|
92
|
+
is_signer: bool = False
|
|
93
|
+
is_writable: bool = False
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@dataclass(frozen=True, slots=True)
|
|
97
|
+
class SolanaInstructionInput:
|
|
98
|
+
program_id: str
|
|
99
|
+
accounts: tuple[SolanaAccountMeta, ...]
|
|
100
|
+
data: str | bytes
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@dataclass(frozen=True, slots=True)
|
|
104
|
+
class TransferSolOperation:
|
|
105
|
+
destination: str
|
|
106
|
+
amount: Amount
|
|
107
|
+
type: Literal["transferSol"] = "transferSol"
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
@dataclass(frozen=True, slots=True)
|
|
111
|
+
class TransferTokenOperation:
|
|
112
|
+
mint: str
|
|
113
|
+
destination_owner: str
|
|
114
|
+
amount: Amount
|
|
115
|
+
type: Literal["transferToken"] = "transferToken"
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
PrepareOperation: TypeAlias = TransferSolOperation | TransferTokenOperation
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@dataclass(frozen=True, slots=True)
|
|
122
|
+
class SwigUsdBalance:
|
|
123
|
+
swig_config_address: str
|
|
124
|
+
wallet_address: str
|
|
125
|
+
usd_value: float
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
@dataclass(frozen=True, slots=True)
|
|
129
|
+
class SwigTokenBalance:
|
|
130
|
+
mint_address: str
|
|
131
|
+
token_program: int
|
|
132
|
+
token_symbol: str
|
|
133
|
+
token_name: str
|
|
134
|
+
decimals: int
|
|
135
|
+
amount_raw: str
|
|
136
|
+
ui_amount: float
|
|
137
|
+
usd_price: float
|
|
138
|
+
usd_value: float
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@dataclass(frozen=True, slots=True)
|
|
142
|
+
class ListSwigTokenBalancesResult:
|
|
143
|
+
swig_config_address: str
|
|
144
|
+
wallet_address: str
|
|
145
|
+
balances: tuple[SwigTokenBalance, ...]
|
|
146
|
+
total_usd_value: float
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
@dataclass(frozen=True, slots=True)
|
|
150
|
+
class SwigTokenTransaction:
|
|
151
|
+
transaction_signature: str
|
|
152
|
+
slot: int
|
|
153
|
+
owner_address: str
|
|
154
|
+
wallet_address: str
|
|
155
|
+
is_subaccount: bool
|
|
156
|
+
token_account_address: str
|
|
157
|
+
mint_address: str
|
|
158
|
+
token_program: int
|
|
159
|
+
direction: Literal["inflow", "outflow"]
|
|
160
|
+
amount_raw: str
|
|
161
|
+
decimals: int
|
|
162
|
+
ui_amount: float
|
|
163
|
+
usd_price: float
|
|
164
|
+
usd_value: float
|
|
165
|
+
token_symbol: str
|
|
166
|
+
token_name: str
|
|
167
|
+
block_time: str | None = None
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
@dataclass(frozen=True, slots=True)
|
|
171
|
+
class ListSwigTokenTransactionsResult:
|
|
172
|
+
swig_config_address: str
|
|
173
|
+
wallet_address: str
|
|
174
|
+
transactions: tuple[SwigTokenTransaction, ...]
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
@dataclass(frozen=True, slots=True)
|
|
178
|
+
class Policy:
|
|
179
|
+
id: str
|
|
180
|
+
name: str
|
|
181
|
+
description: str | None
|
|
182
|
+
authority: Mapping[str, object] | None = None
|
|
183
|
+
actions: tuple[Mapping[str, object], ...] = ()
|
|
184
|
+
guardian_enabled: bool = False
|
|
185
|
+
initial_key_source: str | None = None
|
|
186
|
+
guardian_authority: Mapping[str, object] | None = None
|
|
187
|
+
guardian_key_source: str | None = None
|
|
188
|
+
guardian_delay_seconds: int | str | None = None
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
class WalletsClient:
|
|
192
|
+
def __init__(
|
|
193
|
+
self,
|
|
194
|
+
http: HttpClient,
|
|
195
|
+
default_network: Network | None = None,
|
|
196
|
+
) -> None:
|
|
197
|
+
self._http = http
|
|
198
|
+
self._default_network = default_network
|
|
199
|
+
|
|
200
|
+
async def create(
|
|
201
|
+
self,
|
|
202
|
+
*,
|
|
203
|
+
fee_payer: str,
|
|
204
|
+
policy_id: str | None = None,
|
|
205
|
+
initial_user: WalletAuthority | None = None,
|
|
206
|
+
recovery: RecoveryOptions | None = None,
|
|
207
|
+
network: Network | None = None,
|
|
208
|
+
idempotency_key: str | None = None,
|
|
209
|
+
) -> CreateWalletResult:
|
|
210
|
+
policy = await self.get_policy(policy_id) if policy_id else None
|
|
211
|
+
response = await self._http.post(
|
|
212
|
+
"/transaction/wallet/create",
|
|
213
|
+
{
|
|
214
|
+
"network": to_proto_network(
|
|
215
|
+
require_network(network, self._default_network)
|
|
216
|
+
),
|
|
217
|
+
"feePayer": fee_payer,
|
|
218
|
+
"policyId": policy_id,
|
|
219
|
+
"initialUser": (
|
|
220
|
+
wallet_authority_to_wire(initial_user)
|
|
221
|
+
if initial_user is not None
|
|
222
|
+
else None
|
|
223
|
+
),
|
|
224
|
+
},
|
|
225
|
+
)
|
|
226
|
+
created = normalize_create_wallet_response(response)
|
|
227
|
+
resolved_network = created.network or network or self._default_network
|
|
228
|
+
recovery_setup = (
|
|
229
|
+
_recovery_setup_plan(policy, initial_user, recovery)
|
|
230
|
+
if policy is not None
|
|
231
|
+
else None
|
|
232
|
+
)
|
|
233
|
+
return replace(
|
|
234
|
+
created,
|
|
235
|
+
wallet=replace(created.wallet, network=resolved_network),
|
|
236
|
+
network=resolved_network,
|
|
237
|
+
recovery_setup=recovery_setup,
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
async def get_policy(self, policy_id: str) -> Policy:
|
|
241
|
+
return _normalize_policy(
|
|
242
|
+
await self._http.get(f"/wallet/policies/{quote(policy_id, safe='')}")
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
def use(
|
|
246
|
+
self,
|
|
247
|
+
wallet: str | WalletReference,
|
|
248
|
+
*,
|
|
249
|
+
network: Network | None = None,
|
|
250
|
+
requester_authority: WalletAuthority | None = None,
|
|
251
|
+
) -> WalletHandle:
|
|
252
|
+
if isinstance(wallet, str):
|
|
253
|
+
reference = WalletReference(
|
|
254
|
+
swig_config_address=wallet,
|
|
255
|
+
network=network or self._default_network,
|
|
256
|
+
requester_authority=requester_authority,
|
|
257
|
+
)
|
|
258
|
+
else:
|
|
259
|
+
reference = WalletReference(
|
|
260
|
+
swig_config_address=wallet.swig_config_address,
|
|
261
|
+
wallet_address=wallet.wallet_address,
|
|
262
|
+
network=network or wallet.network or self._default_network,
|
|
263
|
+
requester_authority=(requester_authority or wallet.requester_authority),
|
|
264
|
+
)
|
|
265
|
+
return WalletHandle(self, reference)
|
|
266
|
+
|
|
267
|
+
def from_idp_session(
|
|
268
|
+
self,
|
|
269
|
+
session: IdpWalletSession,
|
|
270
|
+
*,
|
|
271
|
+
network: Network | None = None,
|
|
272
|
+
requester_authority: WalletAuthority | None = None,
|
|
273
|
+
) -> WalletHandle:
|
|
274
|
+
return WalletHandle(
|
|
275
|
+
self,
|
|
276
|
+
WalletReference(
|
|
277
|
+
swig_config_address=session.config_address,
|
|
278
|
+
wallet_address=session.wallet_address,
|
|
279
|
+
network=network or self._default_network,
|
|
280
|
+
requester_authority=(
|
|
281
|
+
requester_authority or session.requester_authority
|
|
282
|
+
),
|
|
283
|
+
),
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
async def prepare(
|
|
287
|
+
self,
|
|
288
|
+
wallet: WalletHandle,
|
|
289
|
+
*,
|
|
290
|
+
fee_payer: str,
|
|
291
|
+
operations: Sequence[PrepareOperation],
|
|
292
|
+
requester_authority: WalletAuthority | None = None,
|
|
293
|
+
network: Network | None = None,
|
|
294
|
+
idempotency_key: str | None = None,
|
|
295
|
+
) -> PreparedTransactionsResult:
|
|
296
|
+
authority = _requester_authority(wallet, requester_authority)
|
|
297
|
+
response = await self._http.post(
|
|
298
|
+
"/transaction/prepare",
|
|
299
|
+
{
|
|
300
|
+
**_base_write_request(
|
|
301
|
+
wallet, fee_payer, network, self._default_network
|
|
302
|
+
),
|
|
303
|
+
"requesterAuthority": wallet_authority_to_wire(authority),
|
|
304
|
+
"operations": [_operation_to_wire(item) for item in operations],
|
|
305
|
+
},
|
|
306
|
+
)
|
|
307
|
+
return normalize_prepared_transactions_result(response)
|
|
308
|
+
|
|
309
|
+
async def transfer_sol(
|
|
310
|
+
self,
|
|
311
|
+
wallet: WalletHandle,
|
|
312
|
+
*,
|
|
313
|
+
fee_payer: str,
|
|
314
|
+
destination: str,
|
|
315
|
+
amount: Amount,
|
|
316
|
+
requester_authority: WalletAuthority | None = None,
|
|
317
|
+
network: Network | None = None,
|
|
318
|
+
idempotency_key: str | None = None,
|
|
319
|
+
) -> PreparedTransaction:
|
|
320
|
+
authority = _requester_authority(wallet, requester_authority)
|
|
321
|
+
return normalize_prepared_transaction(
|
|
322
|
+
await self._http.post(
|
|
323
|
+
"/transaction/transfer/sol",
|
|
324
|
+
{
|
|
325
|
+
**_base_write_request(
|
|
326
|
+
wallet, fee_payer, network, self._default_network
|
|
327
|
+
),
|
|
328
|
+
"requesterAuthority": wallet_authority_to_wire(authority),
|
|
329
|
+
"destination": destination,
|
|
330
|
+
"lamports": normalize_amount(amount),
|
|
331
|
+
},
|
|
332
|
+
)
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
async def transfer_token(
|
|
336
|
+
self,
|
|
337
|
+
wallet: WalletHandle,
|
|
338
|
+
*,
|
|
339
|
+
fee_payer: str,
|
|
340
|
+
mint: str,
|
|
341
|
+
destination_owner: str,
|
|
342
|
+
amount: Amount,
|
|
343
|
+
requester_authority: WalletAuthority | None = None,
|
|
344
|
+
network: Network | None = None,
|
|
345
|
+
idempotency_key: str | None = None,
|
|
346
|
+
) -> PreparedTransaction:
|
|
347
|
+
authority = _requester_authority(wallet, requester_authority)
|
|
348
|
+
return normalize_prepared_transaction(
|
|
349
|
+
await self._http.post(
|
|
350
|
+
"/transaction/transfer/spl-token",
|
|
351
|
+
{
|
|
352
|
+
**_base_write_request(
|
|
353
|
+
wallet, fee_payer, network, self._default_network
|
|
354
|
+
),
|
|
355
|
+
"requesterAuthority": wallet_authority_to_wire(authority),
|
|
356
|
+
"mint": mint,
|
|
357
|
+
"destinationOwner": destination_owner,
|
|
358
|
+
"amount": normalize_amount(amount),
|
|
359
|
+
},
|
|
360
|
+
)
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
async def jupiter_swap(
|
|
364
|
+
self,
|
|
365
|
+
wallet: WalletHandle,
|
|
366
|
+
*,
|
|
367
|
+
fee_payer: str,
|
|
368
|
+
input_mint: str,
|
|
369
|
+
output_mint: str,
|
|
370
|
+
amount: Amount,
|
|
371
|
+
requester_authority: WalletAuthority | None = None,
|
|
372
|
+
slippage_bps: int | None = None,
|
|
373
|
+
destination_account: str | None = None,
|
|
374
|
+
wrap_and_unwrap_sol: bool | None = None,
|
|
375
|
+
tip_amount_lamports: Amount | None = None,
|
|
376
|
+
compute_unit_price_percentile: str | None = None,
|
|
377
|
+
max_accounts: int | None = None,
|
|
378
|
+
mode: str | None = None,
|
|
379
|
+
blockhash_slots_to_expiry: int | None = None,
|
|
380
|
+
network: Network | None = None,
|
|
381
|
+
idempotency_key: str | None = None,
|
|
382
|
+
) -> PreparedTransaction:
|
|
383
|
+
authority = _requester_authority(wallet, requester_authority)
|
|
384
|
+
return normalize_prepared_transaction(
|
|
385
|
+
await self._http.post(
|
|
386
|
+
"/transaction/swap/jupiter",
|
|
387
|
+
{
|
|
388
|
+
**_base_write_request(
|
|
389
|
+
wallet, fee_payer, network, self._default_network
|
|
390
|
+
),
|
|
391
|
+
"requesterAuthority": wallet_authority_to_wire(authority),
|
|
392
|
+
"inputMint": input_mint,
|
|
393
|
+
"outputMint": output_mint,
|
|
394
|
+
"amount": normalize_amount(amount),
|
|
395
|
+
"slippageBps": slippage_bps,
|
|
396
|
+
"destinationAccount": destination_account,
|
|
397
|
+
"wrapAndUnwrapSol": wrap_and_unwrap_sol,
|
|
398
|
+
"tipAmountLamports": (
|
|
399
|
+
normalize_amount(tip_amount_lamports)
|
|
400
|
+
if tip_amount_lamports is not None
|
|
401
|
+
else None
|
|
402
|
+
),
|
|
403
|
+
"computeUnitPricePercentile": compute_unit_price_percentile,
|
|
404
|
+
"maxAccounts": max_accounts,
|
|
405
|
+
"mode": mode,
|
|
406
|
+
"blockhashSlotsToExpiry": blockhash_slots_to_expiry,
|
|
407
|
+
},
|
|
408
|
+
)
|
|
409
|
+
)
|
|
410
|
+
|
|
411
|
+
async def add_recovery_authority(
|
|
412
|
+
self,
|
|
413
|
+
wallet: WalletHandle,
|
|
414
|
+
*,
|
|
415
|
+
fee_payer: str,
|
|
416
|
+
requester_authority: WalletAuthority | None = None,
|
|
417
|
+
network: Network | None = None,
|
|
418
|
+
idempotency_key: str | None = None,
|
|
419
|
+
) -> PreparedTransaction:
|
|
420
|
+
authority = _requester_authority(wallet, requester_authority)
|
|
421
|
+
return normalize_prepared_transaction(
|
|
422
|
+
await self._http.post(
|
|
423
|
+
"/transaction/wallet/recovery-authority/add",
|
|
424
|
+
{
|
|
425
|
+
**_base_write_request(
|
|
426
|
+
wallet, fee_payer, network, self._default_network
|
|
427
|
+
),
|
|
428
|
+
"requesterAuthority": wallet_authority_to_wire(authority),
|
|
429
|
+
},
|
|
430
|
+
)
|
|
431
|
+
)
|
|
432
|
+
|
|
433
|
+
async def configure_recovery(
|
|
434
|
+
self,
|
|
435
|
+
wallet: WalletHandle,
|
|
436
|
+
*,
|
|
437
|
+
fee_payer: str,
|
|
438
|
+
guardian_pubkey: str,
|
|
439
|
+
delay_seconds: int | None = None,
|
|
440
|
+
target_role_id: int | None = None,
|
|
441
|
+
network: Network | None = None,
|
|
442
|
+
idempotency_key: str | None = None,
|
|
443
|
+
) -> PreparedTransaction:
|
|
444
|
+
return normalize_prepared_transaction(
|
|
445
|
+
await self._http.post(
|
|
446
|
+
"/transaction/recovery/configure",
|
|
447
|
+
{
|
|
448
|
+
**_base_write_request(
|
|
449
|
+
wallet, fee_payer, network, self._default_network
|
|
450
|
+
),
|
|
451
|
+
"guardianPubkey": guardian_pubkey,
|
|
452
|
+
"delaySeconds": delay_seconds,
|
|
453
|
+
"targetRoleId": target_role_id,
|
|
454
|
+
},
|
|
455
|
+
)
|
|
456
|
+
)
|
|
457
|
+
|
|
458
|
+
async def prepare_recovery_setup(
|
|
459
|
+
self,
|
|
460
|
+
wallet: WalletHandle,
|
|
461
|
+
*,
|
|
462
|
+
fee_payer: str,
|
|
463
|
+
guardian_pubkey: str,
|
|
464
|
+
requester_authority: WalletAuthority | None = None,
|
|
465
|
+
delay_seconds: int | None = None,
|
|
466
|
+
target_role_id: int | None = None,
|
|
467
|
+
network: Network | None = None,
|
|
468
|
+
idempotency_key: str | None = None,
|
|
469
|
+
) -> PreparedRecoverySetupResult:
|
|
470
|
+
add_authority = await self.add_recovery_authority(
|
|
471
|
+
wallet,
|
|
472
|
+
fee_payer=fee_payer,
|
|
473
|
+
requester_authority=requester_authority,
|
|
474
|
+
network=network,
|
|
475
|
+
idempotency_key=idempotency_key,
|
|
476
|
+
)
|
|
477
|
+
configure = await self.configure_recovery(
|
|
478
|
+
wallet,
|
|
479
|
+
fee_payer=fee_payer,
|
|
480
|
+
guardian_pubkey=guardian_pubkey,
|
|
481
|
+
delay_seconds=delay_seconds,
|
|
482
|
+
target_role_id=target_role_id,
|
|
483
|
+
network=network,
|
|
484
|
+
idempotency_key=idempotency_key,
|
|
485
|
+
)
|
|
486
|
+
transactions = (add_authority, configure)
|
|
487
|
+
resolved_network = network or wallet.network or self._default_network
|
|
488
|
+
wallet_info = add_authority.wallet or configure.wallet
|
|
489
|
+
return PreparedRecoverySetupResult(
|
|
490
|
+
wallet=(
|
|
491
|
+
replace(wallet_info, network=resolved_network)
|
|
492
|
+
if wallet_info is not None
|
|
493
|
+
else None
|
|
494
|
+
),
|
|
495
|
+
transactions=transactions,
|
|
496
|
+
client_authority_transactions=tuple(
|
|
497
|
+
item for item in transactions if item.signature_requests
|
|
498
|
+
),
|
|
499
|
+
operator_signed_transactions=tuple(
|
|
500
|
+
item for item in transactions if item.kind == "configure-recovery"
|
|
501
|
+
),
|
|
502
|
+
fee_payer_only_transactions=tuple(
|
|
503
|
+
item
|
|
504
|
+
for item in transactions
|
|
505
|
+
if not item.signature_requests and item.kind != "configure-recovery"
|
|
506
|
+
),
|
|
507
|
+
add_authority_transaction=add_authority,
|
|
508
|
+
configure_recovery_transaction=configure,
|
|
509
|
+
network=resolved_network,
|
|
510
|
+
)
|
|
511
|
+
|
|
512
|
+
async def start_recovery(
|
|
513
|
+
self,
|
|
514
|
+
wallet: WalletHandle,
|
|
515
|
+
*,
|
|
516
|
+
fee_payer: str,
|
|
517
|
+
guardian_pubkey: str,
|
|
518
|
+
new_authority: str,
|
|
519
|
+
network: Network | None = None,
|
|
520
|
+
idempotency_key: str | None = None,
|
|
521
|
+
) -> PreparedTransaction:
|
|
522
|
+
return normalize_prepared_transaction(
|
|
523
|
+
await self._http.post(
|
|
524
|
+
"/transaction/recovery/start",
|
|
525
|
+
{
|
|
526
|
+
**_base_write_request(
|
|
527
|
+
wallet, fee_payer, network, self._default_network
|
|
528
|
+
),
|
|
529
|
+
"guardianPubkey": guardian_pubkey,
|
|
530
|
+
"newAuthority": new_authority,
|
|
531
|
+
},
|
|
532
|
+
)
|
|
533
|
+
)
|
|
534
|
+
|
|
535
|
+
async def cancel_recovery(
|
|
536
|
+
self,
|
|
537
|
+
wallet: WalletHandle,
|
|
538
|
+
*,
|
|
539
|
+
fee_payer: str,
|
|
540
|
+
requester_authority: WalletAuthority | None = None,
|
|
541
|
+
network: Network | None = None,
|
|
542
|
+
idempotency_key: str | None = None,
|
|
543
|
+
) -> PreparedTransaction:
|
|
544
|
+
authority = _requester_authority(wallet, requester_authority)
|
|
545
|
+
return normalize_prepared_transaction(
|
|
546
|
+
await self._http.post(
|
|
547
|
+
"/transaction/recovery/cancel",
|
|
548
|
+
{
|
|
549
|
+
**_base_write_request(
|
|
550
|
+
wallet, fee_payer, network, self._default_network
|
|
551
|
+
),
|
|
552
|
+
"requesterAuthority": wallet_authority_to_wire(authority),
|
|
553
|
+
},
|
|
554
|
+
)
|
|
555
|
+
)
|
|
556
|
+
|
|
557
|
+
async def execute_recovery(
|
|
558
|
+
self,
|
|
559
|
+
wallet: WalletHandle,
|
|
560
|
+
*,
|
|
561
|
+
fee_payer: str,
|
|
562
|
+
new_authority: str,
|
|
563
|
+
network: Network | None = None,
|
|
564
|
+
idempotency_key: str | None = None,
|
|
565
|
+
) -> PreparedTransaction:
|
|
566
|
+
return normalize_prepared_transaction(
|
|
567
|
+
await self._http.post(
|
|
568
|
+
"/transaction/recovery/execute",
|
|
569
|
+
{
|
|
570
|
+
**_base_write_request(
|
|
571
|
+
wallet, fee_payer, network, self._default_network
|
|
572
|
+
),
|
|
573
|
+
"newAuthority": new_authority,
|
|
574
|
+
},
|
|
575
|
+
)
|
|
576
|
+
)
|
|
577
|
+
|
|
578
|
+
async def execute(
|
|
579
|
+
self,
|
|
580
|
+
wallet: WalletHandle,
|
|
581
|
+
*,
|
|
582
|
+
instructions: Sequence[SolanaInstructionInput],
|
|
583
|
+
address_lookup_table_accounts: Sequence[str] | None = None,
|
|
584
|
+
network: Network | None = None,
|
|
585
|
+
idempotency_key: str | None = None,
|
|
586
|
+
) -> PreparedTransaction:
|
|
587
|
+
return normalize_prepared_transaction(
|
|
588
|
+
await self._http.post(
|
|
589
|
+
f"/v1/wallets/{quote(wallet.swig_config_address, safe='')}/execute",
|
|
590
|
+
{
|
|
591
|
+
"wallet": wallet.swig_config_address,
|
|
592
|
+
"network": network or wallet.network or self._default_network,
|
|
593
|
+
"instructions": [
|
|
594
|
+
_instruction_to_wire(item) for item in instructions
|
|
595
|
+
],
|
|
596
|
+
"addressLookupTableAccounts": address_lookup_table_accounts,
|
|
597
|
+
"idempotencyKey": idempotency_key,
|
|
598
|
+
},
|
|
599
|
+
)
|
|
600
|
+
)
|
|
601
|
+
|
|
602
|
+
async def get_usd_balance(
|
|
603
|
+
self,
|
|
604
|
+
wallet: WalletHandle,
|
|
605
|
+
*,
|
|
606
|
+
network: Network | None = None,
|
|
607
|
+
) -> SwigUsdBalance:
|
|
608
|
+
return _normalize_usd_balance(
|
|
609
|
+
await self._http.get(
|
|
610
|
+
_wallet_read_path(
|
|
611
|
+
wallet,
|
|
612
|
+
"balance/usd",
|
|
613
|
+
network or wallet.network or self._default_network,
|
|
614
|
+
)
|
|
615
|
+
)
|
|
616
|
+
)
|
|
617
|
+
|
|
618
|
+
async def list_token_balances(
|
|
619
|
+
self,
|
|
620
|
+
wallet: WalletHandle,
|
|
621
|
+
*,
|
|
622
|
+
network: Network | None = None,
|
|
623
|
+
) -> ListSwigTokenBalancesResult:
|
|
624
|
+
return _normalize_token_balances(
|
|
625
|
+
await self._http.get(
|
|
626
|
+
_wallet_read_path(
|
|
627
|
+
wallet,
|
|
628
|
+
"token-balances",
|
|
629
|
+
network or wallet.network or self._default_network,
|
|
630
|
+
)
|
|
631
|
+
)
|
|
632
|
+
)
|
|
633
|
+
|
|
634
|
+
async def list_token_transactions(
|
|
635
|
+
self,
|
|
636
|
+
wallet: WalletHandle,
|
|
637
|
+
*,
|
|
638
|
+
network: Network | None = None,
|
|
639
|
+
limit: int | None = None,
|
|
640
|
+
) -> ListSwigTokenTransactionsResult:
|
|
641
|
+
return _normalize_token_transactions(
|
|
642
|
+
await self._http.get(
|
|
643
|
+
_wallet_read_path(
|
|
644
|
+
wallet,
|
|
645
|
+
"token-transactions",
|
|
646
|
+
network or wallet.network or self._default_network,
|
|
647
|
+
limit,
|
|
648
|
+
)
|
|
649
|
+
)
|
|
650
|
+
)
|
|
651
|
+
|
|
652
|
+
|
|
653
|
+
class WalletHandle:
|
|
654
|
+
def __init__(self, wallets: WalletsClient, reference: WalletReference) -> None:
|
|
655
|
+
self._wallets = wallets
|
|
656
|
+
self.swig_config_address = reference.swig_config_address
|
|
657
|
+
self.wallet_address = reference.wallet_address
|
|
658
|
+
self.network = reference.network
|
|
659
|
+
self.requester_authority = reference.requester_authority
|
|
660
|
+
self.transfer = WalletTransferClient(wallets, self)
|
|
661
|
+
self.swap = WalletSwapClient(wallets, self)
|
|
662
|
+
self.recovery = WalletRecoveryClient(wallets, self)
|
|
663
|
+
|
|
664
|
+
async def prepare(
|
|
665
|
+
self,
|
|
666
|
+
*,
|
|
667
|
+
fee_payer: str,
|
|
668
|
+
operations: Sequence[PrepareOperation],
|
|
669
|
+
requester_authority: WalletAuthority | None = None,
|
|
670
|
+
network: Network | None = None,
|
|
671
|
+
idempotency_key: str | None = None,
|
|
672
|
+
) -> PreparedTransactionsResult:
|
|
673
|
+
return await self._wallets.prepare(
|
|
674
|
+
self,
|
|
675
|
+
fee_payer=fee_payer,
|
|
676
|
+
operations=operations,
|
|
677
|
+
requester_authority=requester_authority,
|
|
678
|
+
network=network,
|
|
679
|
+
idempotency_key=idempotency_key,
|
|
680
|
+
)
|
|
681
|
+
|
|
682
|
+
async def execute(
|
|
683
|
+
self,
|
|
684
|
+
*,
|
|
685
|
+
instructions: Sequence[SolanaInstructionInput],
|
|
686
|
+
address_lookup_table_accounts: Sequence[str] | None = None,
|
|
687
|
+
network: Network | None = None,
|
|
688
|
+
idempotency_key: str | None = None,
|
|
689
|
+
) -> PreparedTransaction:
|
|
690
|
+
return await self._wallets.execute(
|
|
691
|
+
self,
|
|
692
|
+
instructions=instructions,
|
|
693
|
+
address_lookup_table_accounts=address_lookup_table_accounts,
|
|
694
|
+
network=network,
|
|
695
|
+
idempotency_key=idempotency_key,
|
|
696
|
+
)
|
|
697
|
+
|
|
698
|
+
async def get_usd_balance(
|
|
699
|
+
self, *, network: Network | None = None
|
|
700
|
+
) -> SwigUsdBalance:
|
|
701
|
+
return await self._wallets.get_usd_balance(self, network=network)
|
|
702
|
+
|
|
703
|
+
async def list_token_balances(
|
|
704
|
+
self, *, network: Network | None = None
|
|
705
|
+
) -> ListSwigTokenBalancesResult:
|
|
706
|
+
return await self._wallets.list_token_balances(self, network=network)
|
|
707
|
+
|
|
708
|
+
async def list_token_transactions(
|
|
709
|
+
self,
|
|
710
|
+
*,
|
|
711
|
+
network: Network | None = None,
|
|
712
|
+
limit: int | None = None,
|
|
713
|
+
) -> ListSwigTokenTransactionsResult:
|
|
714
|
+
return await self._wallets.list_token_transactions(
|
|
715
|
+
self, network=network, limit=limit
|
|
716
|
+
)
|
|
717
|
+
|
|
718
|
+
|
|
719
|
+
class WalletTransferClient:
|
|
720
|
+
def __init__(self, wallets: WalletsClient, wallet: WalletHandle) -> None:
|
|
721
|
+
self._wallets = wallets
|
|
722
|
+
self._wallet = wallet
|
|
723
|
+
|
|
724
|
+
async def __call__(
|
|
725
|
+
self,
|
|
726
|
+
*,
|
|
727
|
+
fee_payer: str,
|
|
728
|
+
amount: Amount,
|
|
729
|
+
destination: str | None = None,
|
|
730
|
+
mint: str | None = None,
|
|
731
|
+
destination_owner: str | None = None,
|
|
732
|
+
requester_authority: WalletAuthority | None = None,
|
|
733
|
+
network: Network | None = None,
|
|
734
|
+
idempotency_key: str | None = None,
|
|
735
|
+
) -> PreparedTransaction:
|
|
736
|
+
if mint:
|
|
737
|
+
if destination_owner is None:
|
|
738
|
+
raise ValueError("destination_owner is required for a token transfer")
|
|
739
|
+
return await self.token(
|
|
740
|
+
fee_payer=fee_payer,
|
|
741
|
+
mint=mint,
|
|
742
|
+
destination_owner=destination_owner,
|
|
743
|
+
amount=amount,
|
|
744
|
+
requester_authority=requester_authority,
|
|
745
|
+
network=network,
|
|
746
|
+
idempotency_key=idempotency_key,
|
|
747
|
+
)
|
|
748
|
+
if destination is None:
|
|
749
|
+
raise ValueError("destination is required for a SOL transfer")
|
|
750
|
+
return await self.sol(
|
|
751
|
+
fee_payer=fee_payer,
|
|
752
|
+
destination=destination,
|
|
753
|
+
amount=amount,
|
|
754
|
+
requester_authority=requester_authority,
|
|
755
|
+
network=network,
|
|
756
|
+
idempotency_key=idempotency_key,
|
|
757
|
+
)
|
|
758
|
+
|
|
759
|
+
async def sol(
|
|
760
|
+
self,
|
|
761
|
+
*,
|
|
762
|
+
fee_payer: str,
|
|
763
|
+
destination: str,
|
|
764
|
+
amount: Amount,
|
|
765
|
+
requester_authority: WalletAuthority | None = None,
|
|
766
|
+
network: Network | None = None,
|
|
767
|
+
idempotency_key: str | None = None,
|
|
768
|
+
) -> PreparedTransaction:
|
|
769
|
+
return await self._wallets.transfer_sol(
|
|
770
|
+
self._wallet,
|
|
771
|
+
fee_payer=fee_payer,
|
|
772
|
+
destination=destination,
|
|
773
|
+
amount=amount,
|
|
774
|
+
requester_authority=requester_authority,
|
|
775
|
+
network=network,
|
|
776
|
+
idempotency_key=idempotency_key,
|
|
777
|
+
)
|
|
778
|
+
|
|
779
|
+
async def token(
|
|
780
|
+
self,
|
|
781
|
+
*,
|
|
782
|
+
fee_payer: str,
|
|
783
|
+
mint: str,
|
|
784
|
+
destination_owner: str,
|
|
785
|
+
amount: Amount,
|
|
786
|
+
requester_authority: WalletAuthority | None = None,
|
|
787
|
+
network: Network | None = None,
|
|
788
|
+
idempotency_key: str | None = None,
|
|
789
|
+
) -> PreparedTransaction:
|
|
790
|
+
return await self._wallets.transfer_token(
|
|
791
|
+
self._wallet,
|
|
792
|
+
fee_payer=fee_payer,
|
|
793
|
+
mint=mint,
|
|
794
|
+
destination_owner=destination_owner,
|
|
795
|
+
amount=amount,
|
|
796
|
+
requester_authority=requester_authority,
|
|
797
|
+
network=network,
|
|
798
|
+
idempotency_key=idempotency_key,
|
|
799
|
+
)
|
|
800
|
+
|
|
801
|
+
async def spl_token(
|
|
802
|
+
self,
|
|
803
|
+
*,
|
|
804
|
+
fee_payer: str,
|
|
805
|
+
mint: str,
|
|
806
|
+
destination_owner: str,
|
|
807
|
+
amount: Amount,
|
|
808
|
+
requester_authority: WalletAuthority | None = None,
|
|
809
|
+
network: Network | None = None,
|
|
810
|
+
idempotency_key: str | None = None,
|
|
811
|
+
) -> PreparedTransaction:
|
|
812
|
+
return await self.token(
|
|
813
|
+
fee_payer=fee_payer,
|
|
814
|
+
mint=mint,
|
|
815
|
+
destination_owner=destination_owner,
|
|
816
|
+
amount=amount,
|
|
817
|
+
requester_authority=requester_authority,
|
|
818
|
+
network=network,
|
|
819
|
+
idempotency_key=idempotency_key,
|
|
820
|
+
)
|
|
821
|
+
|
|
822
|
+
|
|
823
|
+
class WalletSwapClient:
|
|
824
|
+
def __init__(self, wallets: WalletsClient, wallet: WalletHandle) -> None:
|
|
825
|
+
self._wallets = wallets
|
|
826
|
+
self._wallet = wallet
|
|
827
|
+
|
|
828
|
+
async def __call__(
|
|
829
|
+
self,
|
|
830
|
+
*,
|
|
831
|
+
fee_payer: str,
|
|
832
|
+
input_mint: str,
|
|
833
|
+
output_mint: str,
|
|
834
|
+
amount: Amount,
|
|
835
|
+
requester_authority: WalletAuthority | None = None,
|
|
836
|
+
slippage_bps: int | None = None,
|
|
837
|
+
destination_account: str | None = None,
|
|
838
|
+
wrap_and_unwrap_sol: bool | None = None,
|
|
839
|
+
tip_amount_lamports: Amount | None = None,
|
|
840
|
+
compute_unit_price_percentile: str | None = None,
|
|
841
|
+
max_accounts: int | None = None,
|
|
842
|
+
mode: str | None = None,
|
|
843
|
+
blockhash_slots_to_expiry: int | None = None,
|
|
844
|
+
network: Network | None = None,
|
|
845
|
+
idempotency_key: str | None = None,
|
|
846
|
+
) -> PreparedTransaction:
|
|
847
|
+
return await self.jupiter(
|
|
848
|
+
fee_payer=fee_payer,
|
|
849
|
+
input_mint=input_mint,
|
|
850
|
+
output_mint=output_mint,
|
|
851
|
+
amount=amount,
|
|
852
|
+
requester_authority=requester_authority,
|
|
853
|
+
slippage_bps=slippage_bps,
|
|
854
|
+
destination_account=destination_account,
|
|
855
|
+
wrap_and_unwrap_sol=wrap_and_unwrap_sol,
|
|
856
|
+
tip_amount_lamports=tip_amount_lamports,
|
|
857
|
+
compute_unit_price_percentile=compute_unit_price_percentile,
|
|
858
|
+
max_accounts=max_accounts,
|
|
859
|
+
mode=mode,
|
|
860
|
+
blockhash_slots_to_expiry=blockhash_slots_to_expiry,
|
|
861
|
+
network=network,
|
|
862
|
+
idempotency_key=idempotency_key,
|
|
863
|
+
)
|
|
864
|
+
|
|
865
|
+
async def jupiter(
|
|
866
|
+
self,
|
|
867
|
+
*,
|
|
868
|
+
fee_payer: str,
|
|
869
|
+
input_mint: str,
|
|
870
|
+
output_mint: str,
|
|
871
|
+
amount: Amount,
|
|
872
|
+
requester_authority: WalletAuthority | None = None,
|
|
873
|
+
slippage_bps: int | None = None,
|
|
874
|
+
destination_account: str | None = None,
|
|
875
|
+
wrap_and_unwrap_sol: bool | None = None,
|
|
876
|
+
tip_amount_lamports: Amount | None = None,
|
|
877
|
+
compute_unit_price_percentile: str | None = None,
|
|
878
|
+
max_accounts: int | None = None,
|
|
879
|
+
mode: str | None = None,
|
|
880
|
+
blockhash_slots_to_expiry: int | None = None,
|
|
881
|
+
network: Network | None = None,
|
|
882
|
+
idempotency_key: str | None = None,
|
|
883
|
+
) -> PreparedTransaction:
|
|
884
|
+
return await self._wallets.jupiter_swap(
|
|
885
|
+
self._wallet,
|
|
886
|
+
fee_payer=fee_payer,
|
|
887
|
+
input_mint=input_mint,
|
|
888
|
+
output_mint=output_mint,
|
|
889
|
+
amount=amount,
|
|
890
|
+
requester_authority=requester_authority,
|
|
891
|
+
slippage_bps=slippage_bps,
|
|
892
|
+
destination_account=destination_account,
|
|
893
|
+
wrap_and_unwrap_sol=wrap_and_unwrap_sol,
|
|
894
|
+
tip_amount_lamports=tip_amount_lamports,
|
|
895
|
+
compute_unit_price_percentile=compute_unit_price_percentile,
|
|
896
|
+
max_accounts=max_accounts,
|
|
897
|
+
mode=mode,
|
|
898
|
+
blockhash_slots_to_expiry=blockhash_slots_to_expiry,
|
|
899
|
+
network=network,
|
|
900
|
+
idempotency_key=idempotency_key,
|
|
901
|
+
)
|
|
902
|
+
|
|
903
|
+
|
|
904
|
+
class WalletRecoveryClient:
|
|
905
|
+
def __init__(self, wallets: WalletsClient, wallet: WalletHandle) -> None:
|
|
906
|
+
self._wallets = wallets
|
|
907
|
+
self._wallet = wallet
|
|
908
|
+
|
|
909
|
+
async def prepare_setup(
|
|
910
|
+
self,
|
|
911
|
+
*,
|
|
912
|
+
fee_payer: str,
|
|
913
|
+
guardian_pubkey: str,
|
|
914
|
+
requester_authority: WalletAuthority | None = None,
|
|
915
|
+
delay_seconds: int | None = None,
|
|
916
|
+
target_role_id: int | None = None,
|
|
917
|
+
network: Network | None = None,
|
|
918
|
+
idempotency_key: str | None = None,
|
|
919
|
+
) -> PreparedRecoverySetupResult:
|
|
920
|
+
return await self._wallets.prepare_recovery_setup(
|
|
921
|
+
self._wallet,
|
|
922
|
+
fee_payer=fee_payer,
|
|
923
|
+
guardian_pubkey=guardian_pubkey,
|
|
924
|
+
requester_authority=requester_authority,
|
|
925
|
+
delay_seconds=delay_seconds,
|
|
926
|
+
target_role_id=target_role_id,
|
|
927
|
+
network=network,
|
|
928
|
+
idempotency_key=idempotency_key,
|
|
929
|
+
)
|
|
930
|
+
|
|
931
|
+
async def start(
|
|
932
|
+
self,
|
|
933
|
+
*,
|
|
934
|
+
fee_payer: str,
|
|
935
|
+
guardian_pubkey: str,
|
|
936
|
+
new_authority: str,
|
|
937
|
+
network: Network | None = None,
|
|
938
|
+
idempotency_key: str | None = None,
|
|
939
|
+
) -> PreparedTransaction:
|
|
940
|
+
return await self._wallets.start_recovery(
|
|
941
|
+
self._wallet,
|
|
942
|
+
fee_payer=fee_payer,
|
|
943
|
+
guardian_pubkey=guardian_pubkey,
|
|
944
|
+
new_authority=new_authority,
|
|
945
|
+
network=network,
|
|
946
|
+
idempotency_key=idempotency_key,
|
|
947
|
+
)
|
|
948
|
+
|
|
949
|
+
async def cancel(
|
|
950
|
+
self,
|
|
951
|
+
*,
|
|
952
|
+
fee_payer: str,
|
|
953
|
+
requester_authority: WalletAuthority | None = None,
|
|
954
|
+
network: Network | None = None,
|
|
955
|
+
idempotency_key: str | None = None,
|
|
956
|
+
) -> PreparedTransaction:
|
|
957
|
+
return await self._wallets.cancel_recovery(
|
|
958
|
+
self._wallet,
|
|
959
|
+
fee_payer=fee_payer,
|
|
960
|
+
requester_authority=requester_authority,
|
|
961
|
+
network=network,
|
|
962
|
+
idempotency_key=idempotency_key,
|
|
963
|
+
)
|
|
964
|
+
|
|
965
|
+
async def execute(
|
|
966
|
+
self,
|
|
967
|
+
*,
|
|
968
|
+
fee_payer: str,
|
|
969
|
+
new_authority: str,
|
|
970
|
+
network: Network | None = None,
|
|
971
|
+
idempotency_key: str | None = None,
|
|
972
|
+
) -> PreparedTransaction:
|
|
973
|
+
return await self._wallets.execute_recovery(
|
|
974
|
+
self._wallet,
|
|
975
|
+
fee_payer=fee_payer,
|
|
976
|
+
new_authority=new_authority,
|
|
977
|
+
network=network,
|
|
978
|
+
idempotency_key=idempotency_key,
|
|
979
|
+
)
|
|
980
|
+
|
|
981
|
+
|
|
982
|
+
def normalize_create_wallet_response(response: object) -> CreateWalletResult:
|
|
983
|
+
body = _mapping(response, "Create wallet response")
|
|
984
|
+
raw_transactions = body.get("transactions")
|
|
985
|
+
if isinstance(raw_transactions, Sequence) and not isinstance(
|
|
986
|
+
raw_transactions, (str, bytes)
|
|
987
|
+
):
|
|
988
|
+
transactions = tuple(
|
|
989
|
+
normalize_prepared_transaction(item) for item in raw_transactions
|
|
990
|
+
)
|
|
991
|
+
else:
|
|
992
|
+
transactions = (normalize_prepared_transaction(body),)
|
|
993
|
+
creation = next(
|
|
994
|
+
(item for item in transactions if item.kind == "create-swig-wallet"),
|
|
995
|
+
transactions[0] if transactions else None,
|
|
996
|
+
)
|
|
997
|
+
add_authority = next(
|
|
998
|
+
(item for item in transactions if item.kind == "add-authority"), None
|
|
999
|
+
)
|
|
1000
|
+
configure = next(
|
|
1001
|
+
(item for item in transactions if item.kind == "configure-recovery"), None
|
|
1002
|
+
)
|
|
1003
|
+
wallet_value = body.get("wallet")
|
|
1004
|
+
wallet = (
|
|
1005
|
+
wallet_address_info_from_wire(wallet_value)
|
|
1006
|
+
if wallet_value is not None
|
|
1007
|
+
else creation.wallet
|
|
1008
|
+
if creation is not None
|
|
1009
|
+
else None
|
|
1010
|
+
)
|
|
1011
|
+
if wallet is None:
|
|
1012
|
+
raise ValueError("Create wallet response is missing wallet")
|
|
1013
|
+
return CreateWalletResult(
|
|
1014
|
+
wallet=wallet,
|
|
1015
|
+
transactions=transactions,
|
|
1016
|
+
client_authority_transactions=tuple(
|
|
1017
|
+
item for item in transactions if item.signature_requests
|
|
1018
|
+
),
|
|
1019
|
+
operator_signed_transactions=tuple(
|
|
1020
|
+
item for item in transactions if item.kind == "configure-recovery"
|
|
1021
|
+
),
|
|
1022
|
+
fee_payer_only_transactions=tuple(
|
|
1023
|
+
item
|
|
1024
|
+
for item in transactions
|
|
1025
|
+
if item.kind not in ("add-authority", "configure-recovery")
|
|
1026
|
+
),
|
|
1027
|
+
creation_transaction=creation,
|
|
1028
|
+
add_authority_transaction=add_authority,
|
|
1029
|
+
configure_recovery_transaction=configure,
|
|
1030
|
+
network=creation.network if creation else None,
|
|
1031
|
+
)
|
|
1032
|
+
|
|
1033
|
+
|
|
1034
|
+
def _base_write_request(
|
|
1035
|
+
wallet: WalletHandle,
|
|
1036
|
+
fee_payer: str,
|
|
1037
|
+
network: Network | None,
|
|
1038
|
+
default_network: Network | None,
|
|
1039
|
+
) -> dict[str, object]:
|
|
1040
|
+
return {
|
|
1041
|
+
"network": to_proto_network(
|
|
1042
|
+
require_network(network, wallet.network, default_network)
|
|
1043
|
+
),
|
|
1044
|
+
"feePayer": fee_payer,
|
|
1045
|
+
"swigAddress": wallet.swig_config_address,
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
|
|
1049
|
+
def _requester_authority(
|
|
1050
|
+
wallet: WalletHandle,
|
|
1051
|
+
authority: WalletAuthority | None,
|
|
1052
|
+
) -> WalletAuthority:
|
|
1053
|
+
resolved = authority or wallet.requester_authority
|
|
1054
|
+
if resolved is None:
|
|
1055
|
+
raise ValueError("requester_authority is required")
|
|
1056
|
+
return resolved
|
|
1057
|
+
|
|
1058
|
+
|
|
1059
|
+
def _operation_to_wire(operation: PrepareOperation) -> dict[str, object]:
|
|
1060
|
+
if isinstance(operation, TransferSolOperation):
|
|
1061
|
+
return {
|
|
1062
|
+
"transferSol": {
|
|
1063
|
+
"destination": operation.destination,
|
|
1064
|
+
"lamports": normalize_amount(operation.amount),
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
return {
|
|
1068
|
+
"transferToken": {
|
|
1069
|
+
"mint": operation.mint,
|
|
1070
|
+
"destinationOwner": operation.destination_owner,
|
|
1071
|
+
"amount": normalize_amount(operation.amount),
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
|
|
1076
|
+
def _instruction_to_wire(value: SolanaInstructionInput) -> dict[str, object]:
|
|
1077
|
+
data = (
|
|
1078
|
+
value.data
|
|
1079
|
+
if isinstance(value.data, str)
|
|
1080
|
+
else base64.b64encode(value.data).decode("ascii")
|
|
1081
|
+
)
|
|
1082
|
+
return {
|
|
1083
|
+
"programId": value.program_id,
|
|
1084
|
+
"accounts": [
|
|
1085
|
+
{
|
|
1086
|
+
"pubkey": account.pubkey,
|
|
1087
|
+
"isSigner": account.is_signer,
|
|
1088
|
+
"isWritable": account.is_writable,
|
|
1089
|
+
}
|
|
1090
|
+
for account in value.accounts
|
|
1091
|
+
],
|
|
1092
|
+
"data": data,
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
|
|
1096
|
+
def _wallet_read_path(
|
|
1097
|
+
wallet: WalletHandle,
|
|
1098
|
+
route: str,
|
|
1099
|
+
network: Network | None,
|
|
1100
|
+
limit: int | None = None,
|
|
1101
|
+
) -> str:
|
|
1102
|
+
query: dict[str, str] = {}
|
|
1103
|
+
if network is not None:
|
|
1104
|
+
query["network"] = to_proto_network(network)
|
|
1105
|
+
if limit is not None:
|
|
1106
|
+
query["limit"] = str(limit)
|
|
1107
|
+
suffix = f"?{urlencode(query)}" if query else ""
|
|
1108
|
+
return f"/wallet/swig/{quote(wallet.swig_config_address, safe='')}/{route}{suffix}"
|
|
1109
|
+
|
|
1110
|
+
|
|
1111
|
+
def _normalize_usd_balance(value: object) -> SwigUsdBalance:
|
|
1112
|
+
body = _mapping(value, "USD balance response")
|
|
1113
|
+
return SwigUsdBalance(
|
|
1114
|
+
swig_config_address=_required_string(
|
|
1115
|
+
_pick(body, "swigConfigAddress", "swig_config_address"),
|
|
1116
|
+
"swigConfigAddress",
|
|
1117
|
+
),
|
|
1118
|
+
wallet_address=_required_string(
|
|
1119
|
+
_pick(body, "walletAddress", "wallet_address"), "walletAddress"
|
|
1120
|
+
),
|
|
1121
|
+
usd_value=_required_number(_pick(body, "usdValue", "usd_value"), "usdValue"),
|
|
1122
|
+
)
|
|
1123
|
+
|
|
1124
|
+
|
|
1125
|
+
def _normalize_token_balances(value: object) -> ListSwigTokenBalancesResult:
|
|
1126
|
+
body = _mapping(value, "Token balances response")
|
|
1127
|
+
balances = body.get("balances", [])
|
|
1128
|
+
if not isinstance(balances, Sequence) or isinstance(balances, (str, bytes)):
|
|
1129
|
+
raise ValueError("Token balances response has invalid balances")
|
|
1130
|
+
return ListSwigTokenBalancesResult(
|
|
1131
|
+
swig_config_address=_required_string(
|
|
1132
|
+
_pick(body, "swigConfigAddress", "swig_config_address"),
|
|
1133
|
+
"swigConfigAddress",
|
|
1134
|
+
),
|
|
1135
|
+
wallet_address=_required_string(
|
|
1136
|
+
_pick(body, "walletAddress", "wallet_address"), "walletAddress"
|
|
1137
|
+
),
|
|
1138
|
+
balances=tuple(_normalize_token_balance(item) for item in balances),
|
|
1139
|
+
total_usd_value=_required_number(
|
|
1140
|
+
_pick(body, "totalUsdValue", "total_usd_value"), "totalUsdValue"
|
|
1141
|
+
),
|
|
1142
|
+
)
|
|
1143
|
+
|
|
1144
|
+
|
|
1145
|
+
def _normalize_token_balance(value: object) -> SwigTokenBalance:
|
|
1146
|
+
body = _mapping(value, "Token balance")
|
|
1147
|
+
return SwigTokenBalance(
|
|
1148
|
+
mint_address=_required_string(
|
|
1149
|
+
_pick(body, "mintAddress", "mint_address"), "mintAddress"
|
|
1150
|
+
),
|
|
1151
|
+
token_program=_required_int(
|
|
1152
|
+
_pick(body, "tokenProgram", "token_program"), "tokenProgram"
|
|
1153
|
+
),
|
|
1154
|
+
token_symbol=_required_string(
|
|
1155
|
+
_pick(body, "tokenSymbol", "token_symbol") or "", "tokenSymbol"
|
|
1156
|
+
),
|
|
1157
|
+
token_name=_required_string(
|
|
1158
|
+
_pick(body, "tokenName", "token_name") or "", "tokenName"
|
|
1159
|
+
),
|
|
1160
|
+
decimals=_required_int(body.get("decimals"), "decimals"),
|
|
1161
|
+
amount_raw=_required_string(
|
|
1162
|
+
_pick(body, "amountRaw", "amount_raw"), "amountRaw"
|
|
1163
|
+
),
|
|
1164
|
+
ui_amount=_required_number(_pick(body, "uiAmount", "ui_amount"), "uiAmount"),
|
|
1165
|
+
usd_price=_required_number(_pick(body, "usdPrice", "usd_price"), "usdPrice"),
|
|
1166
|
+
usd_value=_required_number(_pick(body, "usdValue", "usd_value"), "usdValue"),
|
|
1167
|
+
)
|
|
1168
|
+
|
|
1169
|
+
|
|
1170
|
+
def _normalize_token_transactions(
|
|
1171
|
+
value: object,
|
|
1172
|
+
) -> ListSwigTokenTransactionsResult:
|
|
1173
|
+
body = _mapping(value, "Token transactions response")
|
|
1174
|
+
transactions = body.get("transactions", [])
|
|
1175
|
+
if not isinstance(transactions, Sequence) or isinstance(transactions, (str, bytes)):
|
|
1176
|
+
raise ValueError("Token transactions response has invalid transactions")
|
|
1177
|
+
return ListSwigTokenTransactionsResult(
|
|
1178
|
+
swig_config_address=_required_string(
|
|
1179
|
+
_pick(body, "swigConfigAddress", "swig_config_address"),
|
|
1180
|
+
"swigConfigAddress",
|
|
1181
|
+
),
|
|
1182
|
+
wallet_address=_required_string(
|
|
1183
|
+
_pick(body, "walletAddress", "wallet_address"), "walletAddress"
|
|
1184
|
+
),
|
|
1185
|
+
transactions=tuple(_normalize_token_transaction(item) for item in transactions),
|
|
1186
|
+
)
|
|
1187
|
+
|
|
1188
|
+
|
|
1189
|
+
def _normalize_token_transaction(value: object) -> SwigTokenTransaction:
|
|
1190
|
+
body = _mapping(value, "Token transaction")
|
|
1191
|
+
direction = body.get("direction")
|
|
1192
|
+
if direction in ("inflow", "SWIG_TOKEN_TRANSACTION_DIRECTION_INFLOW", 1):
|
|
1193
|
+
normalized_direction: Literal["inflow", "outflow"] = "inflow"
|
|
1194
|
+
elif direction in (
|
|
1195
|
+
"outflow",
|
|
1196
|
+
"SWIG_TOKEN_TRANSACTION_DIRECTION_OUTFLOW",
|
|
1197
|
+
2,
|
|
1198
|
+
):
|
|
1199
|
+
normalized_direction = "outflow"
|
|
1200
|
+
else:
|
|
1201
|
+
raise ValueError("Token transaction response has invalid direction")
|
|
1202
|
+
is_subaccount = _pick(body, "isSubaccount", "is_subaccount")
|
|
1203
|
+
if not isinstance(is_subaccount, bool):
|
|
1204
|
+
raise ValueError("Response is missing isSubaccount")
|
|
1205
|
+
return SwigTokenTransaction(
|
|
1206
|
+
transaction_signature=_required_string(
|
|
1207
|
+
_pick(body, "transactionSignature", "transaction_signature"),
|
|
1208
|
+
"transactionSignature",
|
|
1209
|
+
),
|
|
1210
|
+
slot=_required_int(body.get("slot"), "slot"),
|
|
1211
|
+
block_time=_optional_string(_pick(body, "blockTime", "block_time")),
|
|
1212
|
+
owner_address=_required_string(
|
|
1213
|
+
_pick(body, "ownerAddress", "owner_address"), "ownerAddress"
|
|
1214
|
+
),
|
|
1215
|
+
wallet_address=_required_string(
|
|
1216
|
+
_pick(body, "walletAddress", "wallet_address"), "walletAddress"
|
|
1217
|
+
),
|
|
1218
|
+
is_subaccount=is_subaccount,
|
|
1219
|
+
token_account_address=_required_string(
|
|
1220
|
+
_pick(body, "tokenAccountAddress", "token_account_address"),
|
|
1221
|
+
"tokenAccountAddress",
|
|
1222
|
+
),
|
|
1223
|
+
mint_address=_required_string(
|
|
1224
|
+
_pick(body, "mintAddress", "mint_address"), "mintAddress"
|
|
1225
|
+
),
|
|
1226
|
+
token_program=_required_int(
|
|
1227
|
+
_pick(body, "tokenProgram", "token_program"), "tokenProgram"
|
|
1228
|
+
),
|
|
1229
|
+
direction=normalized_direction,
|
|
1230
|
+
amount_raw=_required_string(
|
|
1231
|
+
_pick(body, "amountRaw", "amount_raw"), "amountRaw"
|
|
1232
|
+
),
|
|
1233
|
+
decimals=_required_int(body.get("decimals"), "decimals"),
|
|
1234
|
+
ui_amount=_required_number(_pick(body, "uiAmount", "ui_amount"), "uiAmount"),
|
|
1235
|
+
usd_price=_required_number(_pick(body, "usdPrice", "usd_price"), "usdPrice"),
|
|
1236
|
+
usd_value=_required_number(_pick(body, "usdValue", "usd_value"), "usdValue"),
|
|
1237
|
+
token_symbol=_required_string(
|
|
1238
|
+
_pick(body, "tokenSymbol", "token_symbol") or "", "tokenSymbol"
|
|
1239
|
+
),
|
|
1240
|
+
token_name=_required_string(
|
|
1241
|
+
_pick(body, "tokenName", "token_name") or "", "tokenName"
|
|
1242
|
+
),
|
|
1243
|
+
)
|
|
1244
|
+
|
|
1245
|
+
|
|
1246
|
+
def _normalize_policy(value: object) -> Policy:
|
|
1247
|
+
body = _mapping(value, "Policy response")
|
|
1248
|
+
description = body.get("description")
|
|
1249
|
+
if description is not None and not isinstance(description, str):
|
|
1250
|
+
raise ValueError("Policy response has invalid description")
|
|
1251
|
+
actions_value = body.get("actions", [])
|
|
1252
|
+
if not isinstance(actions_value, Sequence) or isinstance(
|
|
1253
|
+
actions_value, (str, bytes)
|
|
1254
|
+
):
|
|
1255
|
+
raise ValueError("Policy response has invalid actions")
|
|
1256
|
+
actions = tuple(_mapping(item, "Policy action") for item in actions_value)
|
|
1257
|
+
authority = body.get("authority")
|
|
1258
|
+
guardian_authority = body.get("guardianAuthority")
|
|
1259
|
+
guardian_delay_value = body.get("guardianDelaySeconds")
|
|
1260
|
+
guardian_delay = (
|
|
1261
|
+
guardian_delay_value if isinstance(guardian_delay_value, (int, str)) else None
|
|
1262
|
+
)
|
|
1263
|
+
return Policy(
|
|
1264
|
+
id=_required_string(body.get("id"), "id"),
|
|
1265
|
+
name=_required_string(body.get("name"), "name"),
|
|
1266
|
+
description=description,
|
|
1267
|
+
authority=(
|
|
1268
|
+
_mapping(authority, "Policy authority") if authority is not None else None
|
|
1269
|
+
),
|
|
1270
|
+
actions=actions,
|
|
1271
|
+
guardian_enabled=body.get("guardianEnabled") is True,
|
|
1272
|
+
initial_key_source=_optional_string(body.get("initialKeySource")),
|
|
1273
|
+
guardian_authority=(
|
|
1274
|
+
_mapping(guardian_authority, "Policy guardian authority")
|
|
1275
|
+
if guardian_authority is not None
|
|
1276
|
+
else None
|
|
1277
|
+
),
|
|
1278
|
+
guardian_key_source=_optional_string(body.get("guardianKeySource")),
|
|
1279
|
+
guardian_delay_seconds=guardian_delay,
|
|
1280
|
+
)
|
|
1281
|
+
|
|
1282
|
+
|
|
1283
|
+
def _recovery_setup_plan(
|
|
1284
|
+
policy: Policy,
|
|
1285
|
+
initial_user: WalletAuthority | None,
|
|
1286
|
+
recovery: RecoveryOptions | None,
|
|
1287
|
+
) -> RecoverySetupPlan | None:
|
|
1288
|
+
authority = initial_user or _authority_from_policy(policy.authority)
|
|
1289
|
+
guardian = (
|
|
1290
|
+
recovery.guardian_pubkey if recovery else None
|
|
1291
|
+
) or _public_key_from_policy(policy.guardian_authority)
|
|
1292
|
+
if not policy.guardian_enabled or authority is None or guardian is None:
|
|
1293
|
+
return None
|
|
1294
|
+
return RecoverySetupPlan(
|
|
1295
|
+
requester_authority=authority,
|
|
1296
|
+
guardian_pubkey=guardian,
|
|
1297
|
+
delay_seconds=(
|
|
1298
|
+
recovery.delay_seconds
|
|
1299
|
+
if recovery and recovery.delay_seconds is not None
|
|
1300
|
+
else _policy_delay(policy.guardian_delay_seconds)
|
|
1301
|
+
),
|
|
1302
|
+
target_role_id=(recovery.target_role_id if recovery else None),
|
|
1303
|
+
)
|
|
1304
|
+
|
|
1305
|
+
|
|
1306
|
+
def _authority_from_policy(
|
|
1307
|
+
value: Mapping[str, object] | None,
|
|
1308
|
+
) -> WalletAuthority | None:
|
|
1309
|
+
if value is None:
|
|
1310
|
+
return None
|
|
1311
|
+
for scheme in ("ed25519", "secp256k1", "secp256r1"):
|
|
1312
|
+
nested = value.get(scheme)
|
|
1313
|
+
if isinstance(nested, Mapping):
|
|
1314
|
+
public_key = nested.get("publicKey")
|
|
1315
|
+
if isinstance(public_key, str):
|
|
1316
|
+
return {scheme: {"publicKey": public_key}}
|
|
1317
|
+
authority_type = value.get("type")
|
|
1318
|
+
public_key = value.get("publicKey")
|
|
1319
|
+
if not isinstance(public_key, str):
|
|
1320
|
+
return None
|
|
1321
|
+
schemes = {
|
|
1322
|
+
"Ed25519": "ed25519",
|
|
1323
|
+
"Secp256k1": "secp256k1",
|
|
1324
|
+
"Secp256r1": "secp256r1",
|
|
1325
|
+
}
|
|
1326
|
+
policy_scheme = (
|
|
1327
|
+
schemes.get(authority_type) if isinstance(authority_type, str) else None
|
|
1328
|
+
)
|
|
1329
|
+
return {policy_scheme: {"publicKey": public_key}} if policy_scheme else None
|
|
1330
|
+
|
|
1331
|
+
|
|
1332
|
+
def _public_key_from_policy(value: Mapping[str, object] | None) -> str | None:
|
|
1333
|
+
if value is None:
|
|
1334
|
+
return None
|
|
1335
|
+
direct = value.get("publicKey")
|
|
1336
|
+
if isinstance(direct, str):
|
|
1337
|
+
return direct
|
|
1338
|
+
for scheme in ("ed25519", "secp256k1", "secp256r1"):
|
|
1339
|
+
nested = value.get(scheme)
|
|
1340
|
+
if isinstance(nested, Mapping):
|
|
1341
|
+
public_key = nested.get("publicKey")
|
|
1342
|
+
if isinstance(public_key, str):
|
|
1343
|
+
return public_key
|
|
1344
|
+
return None
|
|
1345
|
+
|
|
1346
|
+
|
|
1347
|
+
def _policy_delay(value: int | str | None) -> int:
|
|
1348
|
+
if isinstance(value, int) and value >= 0:
|
|
1349
|
+
return value
|
|
1350
|
+
if isinstance(value, str) and value.isdigit():
|
|
1351
|
+
return int(value)
|
|
1352
|
+
return 86_400
|
|
1353
|
+
|
|
1354
|
+
|
|
1355
|
+
def _mapping(value: object, label: str) -> Mapping[str, object]:
|
|
1356
|
+
if isinstance(value, Mapping):
|
|
1357
|
+
return value
|
|
1358
|
+
raise ValueError(f"{label} must be an object")
|
|
1359
|
+
|
|
1360
|
+
|
|
1361
|
+
def _pick(value: Mapping[str, object], *keys: str) -> object:
|
|
1362
|
+
for key in keys:
|
|
1363
|
+
if key in value:
|
|
1364
|
+
return value[key]
|
|
1365
|
+
return None
|
|
1366
|
+
|
|
1367
|
+
|
|
1368
|
+
def _required_string(value: object, field: str) -> str:
|
|
1369
|
+
if isinstance(value, str):
|
|
1370
|
+
return value
|
|
1371
|
+
raise ValueError(f"Response is missing {field}")
|
|
1372
|
+
|
|
1373
|
+
|
|
1374
|
+
def _optional_string(value: object) -> str | None:
|
|
1375
|
+
return value if isinstance(value, str) else None
|
|
1376
|
+
|
|
1377
|
+
|
|
1378
|
+
def _required_number(value: object, field: str) -> float:
|
|
1379
|
+
if isinstance(value, (int, float)) and not isinstance(value, bool):
|
|
1380
|
+
return float(value)
|
|
1381
|
+
if isinstance(value, str) and value.strip():
|
|
1382
|
+
try:
|
|
1383
|
+
return float(value)
|
|
1384
|
+
except ValueError:
|
|
1385
|
+
pass
|
|
1386
|
+
raise ValueError(f"Response is missing {field}")
|
|
1387
|
+
|
|
1388
|
+
|
|
1389
|
+
def _required_int(value: object, field: str) -> int:
|
|
1390
|
+
number = _required_number(value, field)
|
|
1391
|
+
if not number.is_integer():
|
|
1392
|
+
raise ValueError(f"Response is missing {field}")
|
|
1393
|
+
return int(number)
|