swig-developer-sdk 0.7.0__tar.gz → 0.8.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.
- swig_developer_sdk-0.8.0/.gitignore +17 -0
- swig_developer_sdk-0.8.0/LICENSE.txt +13 -0
- swig_developer_sdk-0.8.0/PKG-INFO +538 -0
- swig_developer_sdk-0.8.0/README.md +512 -0
- {swig_developer_sdk-0.7.0 → swig_developer_sdk-0.8.0}/pyproject.toml +3 -3
- {swig_developer_sdk-0.7.0 → swig_developer_sdk-0.8.0}/src/swig_developer_sdk/__init__.py +44 -24
- {swig_developer_sdk-0.7.0 → swig_developer_sdk-0.8.0}/src/swig_developer_sdk/common.py +1 -1
- {swig_developer_sdk-0.7.0 → swig_developer_sdk-0.8.0}/src/swig_developer_sdk/core.py +13 -5
- {swig_developer_sdk-0.7.0 → swig_developer_sdk-0.8.0}/src/swig_developer_sdk/proxy.py +158 -199
- swig_developer_sdk-0.8.0/src/swig_developer_sdk/ramp.py +684 -0
- {swig_developer_sdk-0.7.0 → swig_developer_sdk-0.8.0}/src/swig_developer_sdk/transactions.py +78 -5
- {swig_developer_sdk-0.7.0 → swig_developer_sdk-0.8.0}/src/swig_developer_sdk/wallets.py +187 -39
- {swig_developer_sdk-0.7.0 → swig_developer_sdk-0.8.0}/tests/test_client.py +28 -0
- swig_developer_sdk-0.8.0/tests/test_proxy.py +247 -0
- swig_developer_sdk-0.8.0/tests/test_resource_clients.py +355 -0
- {swig_developer_sdk-0.7.0 → swig_developer_sdk-0.8.0}/uv.lock +1 -1
- swig_developer_sdk-0.7.0/.gitignore +0 -164
- swig_developer_sdk-0.7.0/PKG-INFO +0 -93
- swig_developer_sdk-0.7.0/README.md +0 -80
- swig_developer_sdk-0.7.0/src/swig_developer_sdk/ramp.py +0 -724
- swig_developer_sdk-0.7.0/tests/test_proxy.py +0 -103
- swig_developer_sdk-0.7.0/tests/test_resource_clients.py +0 -135
- {swig_developer_sdk-0.7.0 → swig_developer_sdk-0.8.0}/scripts/local_transaction_e2e.py +0 -0
- {swig_developer_sdk-0.7.0 → swig_developer_sdk-0.8.0}/src/swig_developer_sdk/client.py +0 -0
- {swig_developer_sdk-0.7.0 → swig_developer_sdk-0.8.0}/src/swig_developer_sdk/evm.py +0 -0
- {swig_developer_sdk-0.7.0 → swig_developer_sdk-0.8.0}/src/swig_developer_sdk/one_business.py +0 -0
- {swig_developer_sdk-0.7.0 → swig_developer_sdk-0.8.0}/src/swig_developer_sdk/passkeys.py +0 -0
- {swig_developer_sdk-0.7.0 → swig_developer_sdk-0.8.0}/src/swig_developer_sdk/paymaster.py +0 -0
- {swig_developer_sdk-0.7.0 → swig_developer_sdk-0.8.0}/src/swig_developer_sdk/signing.py +0 -0
- {swig_developer_sdk-0.7.0 → swig_developer_sdk-0.8.0}/tests/test_helpers.py +0 -0
- {swig_developer_sdk-0.7.0 → swig_developer_sdk-0.8.0}/tests/test_signing.py +0 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Copyright 2026 Swig
|
|
2
|
+
|
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
you may not use this file except in compliance with the License.
|
|
5
|
+
You may obtain a copy of the License at
|
|
6
|
+
|
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
|
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
See the License for the specific language governing permissions and
|
|
13
|
+
limitations under the License.
|
|
@@ -0,0 +1,538 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: swig-developer-sdk
|
|
3
|
+
Version: 0.8.0
|
|
4
|
+
Summary: Python SDK for preparing and locally signing Swig wallet transactions
|
|
5
|
+
Project-URL: Repository, https://github.com/anagrambuild/swig-developer-sdk
|
|
6
|
+
Author: Anagram
|
|
7
|
+
License: Copyright 2026 Swig
|
|
8
|
+
|
|
9
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
10
|
+
you may not use this file except in compliance with the License.
|
|
11
|
+
You may obtain a copy of the License at
|
|
12
|
+
|
|
13
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
14
|
+
|
|
15
|
+
Unless required by applicable law or agreed to in writing, software
|
|
16
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
17
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
18
|
+
See the License for the specific language governing permissions and
|
|
19
|
+
limitations under the License.
|
|
20
|
+
License-File: LICENSE.txt
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Requires-Dist: base58<3,>=2.1
|
|
23
|
+
Requires-Dist: httpx<1,>=0.28
|
|
24
|
+
Requires-Dist: solders<0.29,>=0.28
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# swig-developer-sdk
|
|
28
|
+
|
|
29
|
+
Python SDK for preparing Swig wallet operations on a server and signing them
|
|
30
|
+
locally. The Swig API prepares wallet transactions; this SDK invokes
|
|
31
|
+
application-owned signers and inserts signatures with `solders`. No signing
|
|
32
|
+
material is sent to the API.
|
|
33
|
+
|
|
34
|
+
- Version: `0.8.0`
|
|
35
|
+
- Source: <https://github.com/anagrambuild/swig-developer-sdk>
|
|
36
|
+
- Default API base URL: `https://api.onswig.com`
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install swig-developer-sdk
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
This is the Python parity package for `@swig-wallet/developer-sdk`. Every
|
|
43
|
+
client method is `async`; the transport is `httpx`.
|
|
44
|
+
|
|
45
|
+
## How it works
|
|
46
|
+
|
|
47
|
+
1. Your server creates a `SwigClient` with an API key.
|
|
48
|
+
2. Your server prepares a wallet operation and receives one or more unsigned
|
|
49
|
+
transactions.
|
|
50
|
+
3. Your client signs any transaction whose `signature_requests` is non-empty.
|
|
51
|
+
4. Your app submits the transactions in order, directly or through Swig's
|
|
52
|
+
paymaster sponsorship.
|
|
53
|
+
|
|
54
|
+
The API key stays on the server. Browser code calls a route in your own app —
|
|
55
|
+
see [Proxy handler](#proxy-handler).
|
|
56
|
+
|
|
57
|
+
## Create a client
|
|
58
|
+
|
|
59
|
+
Create an API key from the [Swig dashboard](https://dashboard.onswig.com).
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from swig_developer_sdk import SwigClient
|
|
63
|
+
|
|
64
|
+
swig = SwigClient(api_key="sk_...", network="devnet")
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Requests authenticate with `Authorization: Bearer <api-key>`. Override the base
|
|
68
|
+
URL for non-production deployments:
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
from swig_developer_sdk import RetryOptions, SwigClient
|
|
72
|
+
|
|
73
|
+
swig = SwigClient(
|
|
74
|
+
api_key="sk_...",
|
|
75
|
+
base_url="http://localhost:8080",
|
|
76
|
+
network="devnet",
|
|
77
|
+
retry_options=RetryOptions(max_retries=3, retry_delay=1.0),
|
|
78
|
+
)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Retry behavior
|
|
82
|
+
|
|
83
|
+
- `GET` requests use the configured retry policy.
|
|
84
|
+
- `POST` requests do **not** retry by default, because replaying a preparation
|
|
85
|
+
or submission request can duplicate work.
|
|
86
|
+
- Sponsorship `POST` requests retry only when `idempotency_key` is set. A
|
|
87
|
+
matching retry returns the original paymaster response.
|
|
88
|
+
- `4xx` responses raise `SwigDeveloperSdkError` immediately.
|
|
89
|
+
|
|
90
|
+
## Create a wallet
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
created = await swig.wallets.create(
|
|
94
|
+
fee_payer=fee_payer,
|
|
95
|
+
initial_user={"ed25519": {"publicKey": user_public_key}},
|
|
96
|
+
)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Use `secp256r1` for a passkey initial user or `secp256k1` for an EVM key. Pass
|
|
100
|
+
`policy_id` to create from a portal policy instead of an inline authority. A
|
|
101
|
+
network is required, either on the call or as the client default.
|
|
102
|
+
|
|
103
|
+
The result splits the prepared transactions:
|
|
104
|
+
|
|
105
|
+
| Field | What to do |
|
|
106
|
+
| --- | --- |
|
|
107
|
+
| `wallet` | `swig_config_address`, `wallet_address`, and resolved `network` |
|
|
108
|
+
| `transactions` | submit in this exact order |
|
|
109
|
+
| `client_authority_transactions` | get a client authority signature first |
|
|
110
|
+
| `fee_payer_only_transactions` | send or sponsor without a client authority signature |
|
|
111
|
+
| `creation_transaction` | the create transaction itself |
|
|
112
|
+
|
|
113
|
+
A prepared transaction needs a client authority signature when
|
|
114
|
+
`signature_requests` is non-empty.
|
|
115
|
+
|
|
116
|
+
## Attach to an existing wallet
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
wallet = swig.wallets.use(
|
|
120
|
+
"SWIG_CONFIG_ADDRESS",
|
|
121
|
+
requester_authority={"ed25519": {"publicKey": user_public_key}},
|
|
122
|
+
)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
`swig.wallets.use` also accepts a `WalletReference`, and
|
|
126
|
+
`swig.wallets.from_idp_session(session)` builds the same handle from an IdP
|
|
127
|
+
session.
|
|
128
|
+
|
|
129
|
+
### Prepare transfers and swaps
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
prepared = await wallet.transfer.sol(
|
|
133
|
+
fee_payer=fee_payer,
|
|
134
|
+
destination=destination,
|
|
135
|
+
amount=1_000_000,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
prepared_token = await wallet.transfer.token(
|
|
139
|
+
fee_payer=fee_payer,
|
|
140
|
+
mint=mint,
|
|
141
|
+
destination_owner=destination_owner,
|
|
142
|
+
amount=10_000,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
prepared_swap = await wallet.swap.jupiter(
|
|
146
|
+
fee_payer=fee_payer,
|
|
147
|
+
input_mint=input_mint,
|
|
148
|
+
output_mint=output_mint,
|
|
149
|
+
amount=10_000,
|
|
150
|
+
slippage_bps=100,
|
|
151
|
+
destination_account=destination_account,
|
|
152
|
+
wrap_and_unwrap_sol=True,
|
|
153
|
+
)
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
`wallet.transfer(...)` and `wallet.swap(...)` are callable like their
|
|
157
|
+
TypeScript counterparts; `spl_token` is an alias for `token`.
|
|
158
|
+
|
|
159
|
+
### Prepare grouped operations
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
from swig_developer_sdk import TransferSolOperation, TransferTokenOperation
|
|
163
|
+
|
|
164
|
+
prepared = await wallet.prepare(
|
|
165
|
+
fee_payer=fee_payer,
|
|
166
|
+
operations=(
|
|
167
|
+
TransferSolOperation(destination=destination, amount=1_000_000),
|
|
168
|
+
TransferTokenOperation(
|
|
169
|
+
mint=mint,
|
|
170
|
+
destination_owner=destination_owner,
|
|
171
|
+
amount=10_000,
|
|
172
|
+
),
|
|
173
|
+
),
|
|
174
|
+
)
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Build a custom transaction
|
|
178
|
+
|
|
179
|
+
```python
|
|
180
|
+
from swig_developer_sdk import SolanaAccountMeta, SolanaInstructionInput
|
|
181
|
+
|
|
182
|
+
prepared = await wallet.build_transaction(
|
|
183
|
+
fee_payer=fee_payer,
|
|
184
|
+
instructions=(
|
|
185
|
+
SolanaInstructionInput(
|
|
186
|
+
program_id=program_id,
|
|
187
|
+
accounts=(SolanaAccountMeta(pubkey=destination, is_writable=True),),
|
|
188
|
+
data=instruction_data,
|
|
189
|
+
),
|
|
190
|
+
),
|
|
191
|
+
address_lookup_table_accounts=(lookup_table_address,),
|
|
192
|
+
)
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
The returned transaction is still signed and submitted by your application.
|
|
196
|
+
|
|
197
|
+
## Read wallet state
|
|
198
|
+
|
|
199
|
+
```python
|
|
200
|
+
usd = await wallet.get_usd_balance()
|
|
201
|
+
# usd.swig_config_address, usd.wallet_address, usd.usd_value
|
|
202
|
+
|
|
203
|
+
tokens = await wallet.list_token_balances()
|
|
204
|
+
# tokens.balances[].mint_address, asset_kind, ui_amount, usd_value
|
|
205
|
+
|
|
206
|
+
activity = await wallet.list_token_transactions(limit=25)
|
|
207
|
+
# activity.transactions[].transaction_signature, direction, asset_kind, ui_amount
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Roles
|
|
211
|
+
|
|
212
|
+
`list_roles()` returns the on-chain roles attached to the Swig, which is how
|
|
213
|
+
you inspect who currently holds authority and what each authority may do.
|
|
214
|
+
|
|
215
|
+
```python
|
|
216
|
+
result = await wallet.list_roles()
|
|
217
|
+
|
|
218
|
+
for role in result.roles:
|
|
219
|
+
print(role.role_id, role.authority_type, role.authority_value)
|
|
220
|
+
for action in role.actions:
|
|
221
|
+
print(action.action_index, action.action_code, action.action_data)
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
`authority_value` is the authority's public key material and `authority_type`
|
|
225
|
+
is the protocol authority type discriminant. `action_data` is the raw
|
|
226
|
+
per-action payload, so read it against the protocol's action definitions rather
|
|
227
|
+
than assuming a fixed shape.
|
|
228
|
+
|
|
229
|
+
Policy metadata is a separate read:
|
|
230
|
+
|
|
231
|
+
```python
|
|
232
|
+
policy = await swig.wallets.get_policy(policy_id)
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## Fiat ramps
|
|
236
|
+
|
|
237
|
+
Ramp is split into `swig.ramp.onramp` and `swig.ramp.offramp`. Every ramp call
|
|
238
|
+
requires an `environment` of `"sandbox"` or `"production"`; the SDK encodes it
|
|
239
|
+
as the MELD enum on the wire. Options, quotes, and session creation also require
|
|
240
|
+
`organization_meld_configuration_id`. Quotes additionally require
|
|
241
|
+
`external_customer_id`, `swig_config_address`, and a network (from
|
|
242
|
+
`QuoteRampArgs.network` or the client default).
|
|
243
|
+
|
|
244
|
+
| Client method | Route |
|
|
245
|
+
| --- | --- |
|
|
246
|
+
| `swig.ramp.onramp.get_options` | `GET /wallet/api/ramp/onramp/options` |
|
|
247
|
+
| `swig.ramp.onramp.quote` | `POST /wallet/api/ramp/onramp/quote` |
|
|
248
|
+
| `swig.ramp.onramp.create_session` | `POST /wallet/api/ramp/onramp/session` |
|
|
249
|
+
| `swig.ramp.onramp.get_session` | `GET /wallet/api/ramp/onramp/session/{session_id}` |
|
|
250
|
+
| `swig.ramp.offramp.get_options` | `GET /wallet/api/ramp/offramp/options` |
|
|
251
|
+
| `swig.ramp.offramp.quote` | `POST /wallet/api/ramp/offramp/quote` |
|
|
252
|
+
| `swig.ramp.offramp.create_session` | `POST /wallet/api/ramp/offramp/session` |
|
|
253
|
+
| `swig.ramp.offramp.prepare_authorization` | `POST /wallet/api/ramp/offramp/session/{session_id}/prepare` |
|
|
254
|
+
| `swig.ramp.offramp.submit_authorization` | `POST /wallet/api/ramp/offramp/session/{session_id}/submit` |
|
|
255
|
+
| `swig.ramp.offramp.get_session` | `GET /wallet/api/ramp/offramp/session/{session_id}` |
|
|
256
|
+
|
|
257
|
+
### Onramp
|
|
258
|
+
|
|
259
|
+
```python
|
|
260
|
+
import os
|
|
261
|
+
|
|
262
|
+
from swig_developer_sdk import QuoteRampArgs
|
|
263
|
+
|
|
264
|
+
environment = "sandbox"
|
|
265
|
+
configuration_id = os.environ["SWIG_MELD_CONFIG_ID"]
|
|
266
|
+
|
|
267
|
+
options = await swig.ramp.onramp.get_options(
|
|
268
|
+
organization_meld_configuration_id=configuration_id,
|
|
269
|
+
environment=environment,
|
|
270
|
+
country_code="US",
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
result = await swig.ramp.onramp.quote(
|
|
274
|
+
QuoteRampArgs(
|
|
275
|
+
organization_meld_configuration_id=configuration_id,
|
|
276
|
+
environment=environment,
|
|
277
|
+
external_customer_id=external_customer_id,
|
|
278
|
+
swig_config_address=swig_config_address,
|
|
279
|
+
network="devnet",
|
|
280
|
+
source_amount="100.00",
|
|
281
|
+
source_currency_code=options.fiat_currency_codes[0],
|
|
282
|
+
destination_currency_code=options.crypto_currency_codes[0],
|
|
283
|
+
country_code="US",
|
|
284
|
+
payment_method_type=options.payment_method_types[0],
|
|
285
|
+
)
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
session = await swig.ramp.onramp.create_session(
|
|
289
|
+
organization_meld_configuration_id=configuration_id,
|
|
290
|
+
quote_id=result.quotes[0].quote_id,
|
|
291
|
+
environment=environment,
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
# Send session.launch_url to the user. Treat it as a user-specific secret.
|
|
295
|
+
state = await swig.ramp.onramp.get_session(
|
|
296
|
+
session_id=session.session_id,
|
|
297
|
+
environment=environment,
|
|
298
|
+
)
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
`state.status` is one of `unspecified`, `created`, `pending`, `settling`,
|
|
302
|
+
`settled`, `failed`, `declined`, `cancelled`, or `refunded`.
|
|
303
|
+
|
|
304
|
+
### Offramp
|
|
305
|
+
|
|
306
|
+
Offramp adds an on-chain authorization step: the user's wallet must sign the
|
|
307
|
+
transfer to the provider before the session can settle.
|
|
308
|
+
|
|
309
|
+
```python
|
|
310
|
+
from swig_developer_sdk import QuoteRampArgs, sign_prepared_swig_transaction
|
|
311
|
+
|
|
312
|
+
options = await swig.ramp.offramp.get_options(
|
|
313
|
+
organization_meld_configuration_id=configuration_id,
|
|
314
|
+
environment=environment,
|
|
315
|
+
country_code="US",
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
result = await swig.ramp.offramp.quote(
|
|
319
|
+
QuoteRampArgs(
|
|
320
|
+
organization_meld_configuration_id=configuration_id,
|
|
321
|
+
environment=environment,
|
|
322
|
+
external_customer_id=external_customer_id,
|
|
323
|
+
swig_config_address=swig_config_address,
|
|
324
|
+
network="mainnet",
|
|
325
|
+
source_amount="25.00",
|
|
326
|
+
source_currency_code=options.crypto_currencies[0].currency_code,
|
|
327
|
+
destination_currency_code=options.fiat_currency_codes[0],
|
|
328
|
+
country_code="US",
|
|
329
|
+
payment_method_type=options.payment_method_types[0],
|
|
330
|
+
)
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
session = await swig.ramp.offramp.create_session(
|
|
334
|
+
organization_meld_configuration_id=configuration_id,
|
|
335
|
+
quote_id=result.quotes[0].quote_id,
|
|
336
|
+
environment=environment,
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
authorization = await swig.ramp.offramp.prepare_authorization(
|
|
340
|
+
session_id=session.session_id,
|
|
341
|
+
environment=environment,
|
|
342
|
+
fee_payer=fee_payer,
|
|
343
|
+
requester_authority={"secp256r1": {"publicKey": passkey_public_key}},
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
# Show authorization.display to the user, then sign.
|
|
347
|
+
signed = await sign_prepared_swig_transaction(
|
|
348
|
+
authorization.prepared_transaction,
|
|
349
|
+
secp256r1=application_passkey_signer,
|
|
350
|
+
)
|
|
351
|
+
|
|
352
|
+
submitted = await swig.ramp.offramp.submit_authorization(
|
|
353
|
+
session_id=session.session_id,
|
|
354
|
+
environment=environment,
|
|
355
|
+
authorization_id=authorization.authorization_id,
|
|
356
|
+
signed_transaction=signed.transaction,
|
|
357
|
+
)
|
|
358
|
+
# submitted.solana_signature
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
`authorization.display` carries the human-readable transfer summary
|
|
362
|
+
(`source_amount`, `destination_amount`, `service_provider`, wallet addresses,
|
|
363
|
+
and optional `payment_method_type` / `provider_destination_amount`). Offramp
|
|
364
|
+
sessions add `transfer-required` and `transfer-submitted` to the status set.
|
|
365
|
+
|
|
366
|
+
## Sign locally
|
|
367
|
+
|
|
368
|
+
The generic signer helper works with an application-owned Ed25519 signer. The
|
|
369
|
+
Swig signer helper patches secp256r1 or secp256k1 signatures into both legacy
|
|
370
|
+
and versioned Solana transactions.
|
|
371
|
+
|
|
372
|
+
```python
|
|
373
|
+
from swig_developer_sdk import (
|
|
374
|
+
sign_prepared_swig_transaction,
|
|
375
|
+
sign_prepared_transaction,
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
signed = await sign_prepared_transaction(
|
|
379
|
+
prepared,
|
|
380
|
+
sign_transaction=application_ed25519_signer,
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
signed = await sign_prepared_swig_transaction(
|
|
384
|
+
prepared,
|
|
385
|
+
secp256r1=application_passkey_signer,
|
|
386
|
+
)
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
`sign_prepared_swig_transactions(...)` signs an ordered sequence, which is what
|
|
390
|
+
`created.client_authority_transactions` needs.
|
|
391
|
+
|
|
392
|
+
WebAuthn and EIP-1193 adapters are callback-based, so applications can connect
|
|
393
|
+
a browser, hardware, wallet, or remote signer without changing the preparation
|
|
394
|
+
API:
|
|
395
|
+
|
|
396
|
+
```python
|
|
397
|
+
from swig_developer_sdk import (
|
|
398
|
+
create_secp256k1_evm_signing_fn,
|
|
399
|
+
create_secp256r1_passkey_signing_fn,
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
passkey_signer = create_secp256r1_passkey_signing_fn(get_webauthn_assertion)
|
|
403
|
+
evm_signer = create_secp256k1_evm_signing_fn(
|
|
404
|
+
provider=eip1193_provider,
|
|
405
|
+
address=evm_address,
|
|
406
|
+
)
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
## Submit and sponsor
|
|
410
|
+
|
|
411
|
+
```python
|
|
412
|
+
from swig_developer_sdk import SponsorSignedTransactionArgs
|
|
413
|
+
|
|
414
|
+
submitted = await swig.transactions.sponsor(
|
|
415
|
+
SponsorSignedTransactionArgs(
|
|
416
|
+
transaction=signed.transaction,
|
|
417
|
+
network="mainnet",
|
|
418
|
+
idempotency_key=idempotency_key,
|
|
419
|
+
)
|
|
420
|
+
)
|
|
421
|
+
# submitted.request_id, submitted.signature, submitted.spent_by_paymaster
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
Pass `idempotency_key` whenever your application may retry; that is the only
|
|
425
|
+
case in which the SDK retries a sponsorship POST.
|
|
426
|
+
|
|
427
|
+
For single-transaction sponsorship, `network` resolves from the call and then
|
|
428
|
+
the client default. If neither is set, the paymaster defaults to mainnet.
|
|
429
|
+
|
|
430
|
+
A returned signature means the Solana RPC accepted the transaction. It may
|
|
431
|
+
still be pending and is not confirmation or finality; track it through your
|
|
432
|
+
RPC provider when your product needs either.
|
|
433
|
+
|
|
434
|
+
### Bundle sponsorship
|
|
435
|
+
|
|
436
|
+
```python
|
|
437
|
+
from swig_developer_sdk import SponsorSignedTransactionBundleArgs
|
|
438
|
+
|
|
439
|
+
bundle = await swig.transactions.sponsor_bundle(
|
|
440
|
+
SponsorSignedTransactionBundleArgs(
|
|
441
|
+
transactions=(signed_create.transaction, signed_add_authority.transaction),
|
|
442
|
+
network="mainnet",
|
|
443
|
+
idempotency_key=idempotency_key,
|
|
444
|
+
)
|
|
445
|
+
)
|
|
446
|
+
# bundle.request_id, bundle.bundle_id, bundle.signatures,
|
|
447
|
+
# bundle.estimated_spent_by_paymaster
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
Constraints enforced before the request is sent:
|
|
451
|
+
|
|
452
|
+
- mainnet only — any other network raises `ValueError`
|
|
453
|
+
- one to five transactions per bundle
|
|
454
|
+
- `signatures` come back in bundle order
|
|
455
|
+
- `estimated_spent_by_paymaster` is an estimate, not a settled charge
|
|
456
|
+
|
|
457
|
+
A returned `bundle_id` means Jito accepted the bundle. It may still be pending;
|
|
458
|
+
acceptance is not confirmation or finality.
|
|
459
|
+
|
|
460
|
+
## Paymaster balance
|
|
461
|
+
|
|
462
|
+
```python
|
|
463
|
+
balance = await swig.paymaster.get_balance(network="mainnet")
|
|
464
|
+
idp_balance = await swig.paymaster.get_idp_balance(network="mainnet")
|
|
465
|
+
# balance.configured, balance.address, balance.balance_lamports, balance.balance_sol
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
## Proxy handler
|
|
469
|
+
|
|
470
|
+
Python ships a framework-neutral proxy handler instead of one framework
|
|
471
|
+
adapter. Adapt your framework's request and response objects at the route
|
|
472
|
+
boundary:
|
|
473
|
+
|
|
474
|
+
```python
|
|
475
|
+
from swig_developer_sdk import SwigProxyConfig, create_swig_proxy_handler
|
|
476
|
+
|
|
477
|
+
swig_handler = create_swig_proxy_handler(
|
|
478
|
+
SwigProxyConfig(api_key=swig_api_key, network="devnet")
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
response = await swig_handler.handle(
|
|
482
|
+
method="POST",
|
|
483
|
+
path="/transfer/sol",
|
|
484
|
+
body=request_body,
|
|
485
|
+
)
|
|
486
|
+
# return response.body with response.status
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
The handler covers the same routes as the TypeScript adapters: wallet create,
|
|
490
|
+
grouped prepare, SOL and SPL transfers, Jupiter swap, wallet USD balance, token
|
|
491
|
+
balances, token transactions, roles, paymaster balance, and the onramp and
|
|
492
|
+
offramp routes.
|
|
493
|
+
|
|
494
|
+
`SwigProxyConfig` accepts `api_key`, `transaction_api_url`, `network`,
|
|
495
|
+
`fee_payer` (a value or a callable resolved per request),
|
|
496
|
+
`resolve_requester_authority`, and an optional `httpx` transport. It falls back
|
|
497
|
+
to `SWIG_DEVELOPER_API_KEY` / `SWIG_API_KEY`, `SWIG_TRANSACTION_API_URL`, and
|
|
498
|
+
`SWIG_FEE_PAYER` when those fields are unset. A fee payer is required for every
|
|
499
|
+
preparation route; the handler returns a `400` body when it cannot resolve one.
|
|
500
|
+
|
|
501
|
+
## Errors
|
|
502
|
+
|
|
503
|
+
```python
|
|
504
|
+
from swig_developer_sdk import SwigDeveloperSdkError
|
|
505
|
+
|
|
506
|
+
try:
|
|
507
|
+
await wallet.transfer.sol(
|
|
508
|
+
fee_payer=fee_payer,
|
|
509
|
+
destination=destination,
|
|
510
|
+
amount=1_000_000,
|
|
511
|
+
)
|
|
512
|
+
except SwigDeveloperSdkError as error:
|
|
513
|
+
print(error.status_code, error.code, error)
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
Keep API keys, signed transactions, and ramp launch URLs out of logs.
|
|
517
|
+
|
|
518
|
+
## Local end-to-end script
|
|
519
|
+
|
|
520
|
+
`scripts/local_transaction_e2e.py` exercises wallet creation, real P-256
|
|
521
|
+
signing, direct and grouped SOL transfers, an SPL-token transfer, the Python
|
|
522
|
+
proxy, the live paymaster balance endpoint, and a sponsored transfer where the
|
|
523
|
+
user pays no network fee. Every transaction is submitted to Surfpool and
|
|
524
|
+
verified on-chain; the paymaster flow also retries with the same idempotency
|
|
525
|
+
key and verifies that no balance change repeats.
|
|
526
|
+
|
|
527
|
+
It requires a locally running backend stack, and defaults to
|
|
528
|
+
`http://localhost:8080` for the Developer API and `http://localhost:8899` for
|
|
529
|
+
Surfpool. Override with `SWIG_TRANSACTION_API_URL`, `SOLANA_RPC_URL`, or
|
|
530
|
+
`SWIG_DATABASE_URL`.
|
|
531
|
+
|
|
532
|
+
Jupiter is not covered by this script, since it needs a mainnet-backed
|
|
533
|
+
Surfpool.
|
|
534
|
+
|
|
535
|
+
## Parity
|
|
536
|
+
|
|
537
|
+
See the repository [parity matrix](../PARITY.md) for the complete mapped
|
|
538
|
+
surface against `@swig-wallet/developer-sdk`.
|