cdp-docs-cli 1.0.0

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.
@@ -0,0 +1,741 @@
1
+ # Wallet API v2: Quickstart
2
+
3
+ ## Overview
4
+
5
+ The v2 Wallet API allows you to create [accounts](/wallet-api/v2/introduction/accounts) on EVM compatible networks and the Solana network.
6
+
7
+ In this quickstart, you will learn how to:
8
+
9
+ * Create EVM and Solana accounts
10
+ * Fund your accounts with testnet tokens using CDP Faucets
11
+ * Send a transaction using `viem` for Typescript or `web3` for Python
12
+
13
+ ## Prerequisites
14
+
15
+ Setup all dependencies, export your keys to environment variables, and initialize a new project before you begin.
16
+
17
+ It is assumed you have:
18
+
19
+ * [Node.js](https://nodejs.org/en) 22.x+ if using Typescript
20
+ * [Python](https://www.python.org/downloads/) 3.10+ if using Python
21
+ * [Created](https://portal.cdp.coinbase.com/create-account) and [signed in](https://portal.cdp.coinbase.com/signin) to an existing CDP account
22
+
23
+ Once you have setup the prerequisite dependencies, continue reading to create keys to authenticate your requests and initialize a new project.
24
+
25
+ ### Create keys
26
+
27
+ Sign in to the [CDP Portal](https://portal.cdp.coinbase.com), [create a CDP API key](https://portal.cdp.coinbase.com/projects/api-keys) and [generate a Wallet Secret](https://portal.cdp.coinbase.com/products/wallet-api).
28
+ Keep these values handy as you will need them in the following steps.
29
+
30
+ For more information, see the [CDP API Keys](/get-started/authentication/cdp-api-keys#secret-api-keys) and [Wallet Secret](/wallet-api/v2/introduction/security#wallet-secret) documentation.
31
+
32
+ ### Project setup
33
+
34
+ After creating your keys, initialize a new project and instantiate the CDP client.
35
+
36
+ <Tabs groupId="programming-language">
37
+ <Tab value="Typescript" title="Typescript" default>
38
+ Initialize a new Typescript project by running:
39
+
40
+ ```bash
41
+ mkdir cdp-sdk-example && cd cdp-sdk-example && npm init -y && npm pkg set type="module" && touch main.ts && touch .env
42
+ ```
43
+
44
+ Add your CDP API key and wallet secret to the `.env` file:
45
+
46
+ ```bash .env
47
+ CDP_API_KEY_ID=your-api-key-id
48
+ CDP_API_KEY_SECRET=your-api-key-secret
49
+ CDP_WALLET_SECRET=your-wallet-secret
50
+ ```
51
+
52
+ Now, install the [CDP SDK](https://github.com/coinbase/cdp-sdk) and the [dotenv](https://www.npmjs.com/package/dotenv) packages:
53
+
54
+ ```bash
55
+ npm install @coinbase/cdp-sdk dotenv
56
+ ```
57
+
58
+ Finally, in `main.ts`, instantiate the CDP client:
59
+
60
+ ```typescript main.ts lines wrap [expandable]
61
+ import { CdpClient } from "@coinbase/cdp-sdk";
62
+ import dotenv from "dotenv";
63
+
64
+ dotenv.config();
65
+
66
+ // Initialize the CDP client, which automatically loads
67
+ // the API Key and Wallet Secret from the environment
68
+ // variables.
69
+ const cdp = new CdpClient();
70
+ ```
71
+
72
+ In this and in the following examples, you can run your code by running:
73
+
74
+ ```bash
75
+ npx tsx main.ts
76
+ ```
77
+ </Tab>
78
+
79
+ <Tab value="Python" title="Python">
80
+ Initialize a new Python project by running:
81
+
82
+ ```bash
83
+ mkdir cdp-sdk-example && cd cdp-sdk-example && python -m venv .venv && source .venv/bin/activate && touch main.py && touch .env
84
+ ```
85
+
86
+ Add your CDP API key and wallet secret to the `.env` file:
87
+
88
+ ```bash .env
89
+ CDP_API_KEY_ID=your-api-key-id
90
+ CDP_API_KEY_SECRET=your-api-key-secret
91
+ CDP_WALLET_SECRET=your-wallet-secret
92
+ ```
93
+
94
+ Now, install the [CDP SDK](https://github.com/coinbase/cdp-sdk) and the [python-dotenv](https://pypi.org/project/python-dotenv/) package:
95
+
96
+ ```bash
97
+ pip install cdp-sdk python-dotenv
98
+ ```
99
+
100
+ Finally, in `main.py`, instantiate the CDP client:
101
+
102
+ ```python title="main.py"
103
+ from cdp import CdpClient
104
+ import asyncio
105
+ from dotenv import load_dotenv
106
+
107
+ load_dotenv()
108
+
109
+ async def main():
110
+ # Initialize the CDP client, which automatically loads
111
+ # the API Key and Wallet Secret from the environment
112
+ # variables.
113
+ cdp = CdpClient()
114
+ await cdp.close()
115
+
116
+
117
+ asyncio.run(main())
118
+ ```
119
+
120
+ In this and in the following examples, you can run your code by running:
121
+
122
+ ```bash
123
+ python main.py
124
+ ```
125
+ </Tab>
126
+ </Tabs>
127
+
128
+ ## 1. Create an account
129
+
130
+ The v2 Wallet API offers support for both [EVM compatible accounts and Solana accounts](/wallet-api/v2/introduction/accounts).
131
+
132
+ ### EVM
133
+
134
+ To create an EVM account, see below:
135
+
136
+ <CodeGroup>
137
+ ```typescript main.ts lines wrap
138
+ import { CdpClient } from "@coinbase/cdp-sdk";
139
+ import dotenv from "dotenv";
140
+
141
+ dotenv.config();
142
+
143
+ const cdp = new CdpClient();
144
+ const account = await cdp.evm.createAccount();
145
+ console.log(`Created EVM account: ${account.address}`);
146
+ ```
147
+
148
+ ```python main.py lines wrap
149
+ import asyncio
150
+ from cdp import CdpClient
151
+ from dotenv import load_dotenv
152
+
153
+ load_dotenv()
154
+
155
+ async def main():
156
+ cdp = CdpClient()
157
+ account = await cdp.evm.create_account()
158
+ print(f"Created EVM account: {account.address}")
159
+ await cdp.close()
160
+
161
+
162
+ asyncio.run(main())
163
+ ```
164
+ </CodeGroup>
165
+
166
+ After running the above snippet, you should see similar output:
167
+
168
+ ```console lines wrap
169
+ Created EVM account: 0x3c0D84055994c3062819Ce8730869D0aDeA4c3Bf
170
+ ```
171
+
172
+ <Tip>
173
+ You can also create accounts with human-readable names and retrieve them later using the `getOrCreateAccount` method.
174
+
175
+ See the [Managing Accounts](/wallet-api/v2/using-the-wallet-api/managing-accounts#account-names) guide for more information.
176
+ </Tip>
177
+
178
+ ### Solana
179
+
180
+ To create a Solana account, see below:
181
+
182
+ <CodeGroup>
183
+ ```typescript main.ts lines wrap
184
+ import { CdpClient } from "@coinbase/cdp-sdk";
185
+ import dotenv from "dotenv";
186
+
187
+ dotenv.config();
188
+
189
+ const cdp = new CdpClient();
190
+ const account = await cdp.solana.createAccount();
191
+ console.log(`Created Solana account: ${account.address}`);
192
+ ```
193
+
194
+ ```python main.py lines wrap
195
+ import asyncio
196
+ from cdp import CdpClient
197
+ from dotenv import load_dotenv
198
+
199
+ load_dotenv()
200
+
201
+ async def main():
202
+ cdp = CdpClient()
203
+ account = await cdp.solana.create_account()
204
+ print(f"Created Solana account: {account.address}")
205
+ await cdp.close()
206
+
207
+
208
+ asyncio.run(main())
209
+ ```
210
+ </CodeGroup>
211
+
212
+ After running the above snippet, you should see similar output:
213
+
214
+ ```console lines wrap
215
+ Created Solana account: 2XBS6naS1v7pXEg25z43FGHnmEgEad53fmiZ9S6LPgKn
216
+ ```
217
+
218
+ ## 2. Fund account with test funds
219
+
220
+ Accounts do not have funds on creation. We provide a [Faucet API](/faucets/introduction/welcome) to easily fund your EVM account with testnet tokens and Solana account with devnet tokens.
221
+
222
+ <Info>
223
+ Before you request funds, ensure you read about [rate limits when using CDP Faucets](/faucets/introduction/welcome#supported-assets).
224
+ </Info>
225
+
226
+ ### EVM
227
+
228
+ <CodeGroup>
229
+ ```typescript main.ts lines wrap
230
+ import { CdpClient } from "@coinbase/cdp-sdk";
231
+ import dotenv from "dotenv";
232
+
233
+ dotenv.config();
234
+
235
+ const cdp = new CdpClient();
236
+
237
+ const account = await cdp.evm.createAccount();
238
+ const faucetResponse = await cdp.evm.requestFaucet({
239
+ address: account.address,
240
+ network: "base-sepolia",
241
+ token: "eth"
242
+ });
243
+ console.log(`Requested funds from ETH faucet: https://sepolia.basescan.org/tx/${faucetResponse.transactionHash}`);
244
+ ```
245
+
246
+ ```python main.py lines wrap
247
+ import asyncio
248
+ from cdp import CdpClient
249
+ from dotenv import load_dotenv
250
+
251
+ load_dotenv()
252
+
253
+ async def main():
254
+ cdp = CdpClient()
255
+
256
+ account = await cdp.evm.create_account()
257
+
258
+ faucet_hash = await cdp.evm.request_faucet(
259
+ address=account.address,
260
+ network="base-sepolia",
261
+ token="eth"
262
+ )
263
+ print(f"Requested funds from ETH faucet: https://sepolia.basescan.org/tx/{faucet_hash}")
264
+
265
+ await cdp.close()
266
+
267
+
268
+ asyncio.run(main())
269
+ ```
270
+ </CodeGroup>
271
+
272
+ After running the above, you should see similar output:
273
+
274
+ ```console lines wrap
275
+ Requested funds from ETH faucet: https://sepolia.basescan.org/tx/0x9e93a16f2ca67f35bcb1ea2933f19035ae1e71ff3100d2abc6a22ce024d085ec
276
+ ```
277
+
278
+ ### Solana
279
+
280
+ <CodeGroup>
281
+ ```typescript main.ts lines wrap
282
+ import { CdpClient } from "@coinbase/cdp-sdk";
283
+ import dotenv from "dotenv";
284
+
285
+ dotenv.config();
286
+
287
+ const cdp = new CdpClient();
288
+
289
+ const account = await cdp.solana.createAccount();
290
+
291
+ const { signature } = await cdp.solana.requestFaucet({
292
+ address: account.address,
293
+ token: "sol"
294
+ });
295
+ console.log(`Requested funds from Solana faucet: https://explorer.solana.com/tx/${signature}?cluster=devnet`);
296
+ ```
297
+
298
+ ```python main.py lines wrap
299
+ import asyncio
300
+ from cdp import CdpClient
301
+ from dotenv import load_dotenv
302
+
303
+ load_dotenv()
304
+
305
+ async def main():
306
+ cdp = CdpClient()
307
+
308
+ account = await cdp.solana.create_account()
309
+
310
+ tx_signature_response = await cdp.solana.request_faucet(
311
+ address=account.address,
312
+ token="sol"
313
+ )
314
+
315
+ print(f"Requested funds from Solana faucet: https://explorer.solana.com/tx/{tx_signature_response.transaction_signature}?cluster=devnet")
316
+
317
+ await cdp.close()
318
+
319
+
320
+ asyncio.run(main())
321
+ ```
322
+ </CodeGroup>
323
+
324
+ After running the above, you should see similar output:
325
+
326
+ ```console lines wrap
327
+ Requested funds from Solana faucet: https://explorer.solana.com/tx/4KEPbhkRLTg2FJNqV5bbUd6zv1TNkksxF9PDHw2FodrTha3jq2Cojn4hSKtjPWdrZiRDuYp7okRuc1oYvh3JkLuE?cluster=devnet
328
+ ```
329
+
330
+ ## 3. Send a transaction
331
+
332
+ ### EVM
333
+
334
+ <Tabs groupId="programming-language">
335
+ <Tab value="Typescript" title="Typescript" default>
336
+ You can send transactions using the v2 Wallet API.
337
+
338
+ Note that in order to wait for transaction confirmation, you will need to have `viem` installed:
339
+
340
+ ```bash
341
+ npm install viem
342
+ ```
343
+
344
+ In the example below, we:
345
+
346
+ 1. Create a new EVM account.
347
+ 2. Request ETH from the faucet.
348
+ 3. Use the v2 Wallet API to send a transaction.
349
+ 4. Wait for transaction confirmation.
350
+
351
+ ```typescript main.ts lines wrap [expandable]
352
+ import { CdpClient } from "@coinbase/cdp-sdk";
353
+ import { http, createPublicClient, parseEther } from "viem";
354
+ import { baseSepolia } from "viem/chains";
355
+ import dotenv from "dotenv";
356
+
357
+ dotenv.config();
358
+
359
+ const cdp = new CdpClient();
360
+
361
+ const publicClient = createPublicClient({
362
+ chain: baseSepolia,
363
+ transport: http(),
364
+ });
365
+
366
+ // Step 1: Create a new EVM account.
367
+ const account = await cdp.evm.createAccount();
368
+ console.log("Successfully created EVM account:", account.address);
369
+
370
+ // Step 2: Request ETH from the faucet.
371
+ const { transactionHash: faucetTransactionHash } = await cdp.evm.requestFaucet({
372
+ address: account.address,
373
+ network: "base-sepolia",
374
+ token: "eth",
375
+ });
376
+
377
+ const faucetTxReceipt = await publicClient.waitForTransactionReceipt({
378
+ hash: faucetTransactionHash,
379
+ });
380
+ console.log("Successfully requested ETH from faucet:", faucetTxReceipt.transactionHash);
381
+
382
+ // Step 3: Use the v2 Wallet API to send a transaction.
383
+ const transactionResult = await cdp.evm.sendTransaction({
384
+ address: account.address,
385
+ transaction: {
386
+ to: "0x0000000000000000000000000000000000000000",
387
+ value: parseEther("0.000001"),
388
+ },
389
+ network: "base-sepolia",
390
+ });
391
+
392
+ // Step 4: Wait for the transaction to be confirmed
393
+ const txReceipt = await publicClient.waitForTransactionReceipt({
394
+ hash: transactionResult.transactionHash,
395
+ });
396
+
397
+ console.log(
398
+ `Transaction sent! Link: https://sepolia.basescan.org/tx/${transactionResult.transactionHash}`
399
+ );
400
+ ```
401
+ </Tab>
402
+
403
+ <Tab value="Python" title="Python">
404
+ You can send transactions using the v2 Wallet API.
405
+
406
+ Note that in order to wait for transaction confirmation, you will need to have `web3` installed:
407
+
408
+ ```bash
409
+ pip install web3
410
+ ```
411
+
412
+ In the example below, we:
413
+
414
+ 1. Create a new EVM account.
415
+ 2. Request ETH from the faucet.
416
+ 3. Use the v2 Wallet API to send a transaction.
417
+ 4. Wait for transaction confirmation.
418
+
419
+ ```python main.py lines wrap [expandable]
420
+ import asyncio
421
+
422
+ from cdp import CdpClient
423
+ from cdp.evm_transaction_types import TransactionRequestEIP1559
424
+
425
+ from dotenv import load_dotenv
426
+ from web3 import Web3
427
+
428
+ load_dotenv()
429
+
430
+ w3 = Web3(Web3.HTTPProvider("https://sepolia.base.org"))
431
+
432
+
433
+ async def main():
434
+ async with CdpClient() as cdp:
435
+ account = await cdp.evm.create_account()
436
+ print(f"Created account: {account.address}")
437
+
438
+ faucet_hash = await cdp.evm.request_faucet(
439
+ address=account.address, network="base-sepolia", token="eth"
440
+ )
441
+
442
+ w3.eth.wait_for_transaction_receipt(faucet_hash)
443
+ print(f"Received funds from faucet for address: {account.address}")
444
+
445
+ tx_hash = await cdp.evm.send_transaction(
446
+ address=account.address,
447
+ transaction=TransactionRequestEIP1559(
448
+ to="0x0000000000000000000000000000000000000000",
449
+ value=w3.to_wei(0.000001, "ether"),
450
+ ),
451
+ network="base-sepolia",
452
+ )
453
+
454
+
455
+ print(f"Transaction sent! Link: https://sepolia.basescan.org/tx/{tx_hash}")
456
+
457
+
458
+ asyncio.run(main())
459
+ ```
460
+ </Tab>
461
+ </Tabs>
462
+
463
+ ### Solana
464
+
465
+ <Tabs groupId="programming-language">
466
+ <Tab value="Typescript" title="Typescript" default>
467
+ You can send transactions on Solana using the [`@solana/web3.js`](https://solana.com/docs/clients/javascript) v1 library.
468
+
469
+ ```bash
470
+ npm install @solana/web3.js@1
471
+ ```
472
+
473
+ In the example below, we:
474
+
475
+ 1. Create a new Solana account.
476
+ 2. Request SOL from the faucet.
477
+ 3. Wait for funds to become available.
478
+ 4. Send the transaction to a specified address.
479
+
480
+ ```typescript main.ts lines wrap [expandable]
481
+ import {
482
+ Connection,
483
+ PublicKey,
484
+ SystemProgram,
485
+ Transaction,
486
+ } from "@solana/web3.js";
487
+ import { CdpClient } from "@coinbase/cdp-sdk";
488
+ import dotenv from "dotenv";
489
+
490
+ dotenv.config();
491
+
492
+ const cdp = new CdpClient();
493
+
494
+ const connection = new Connection("https://api.devnet.solana.com");
495
+
496
+ async function createAccount() {
497
+ const account = await cdp.solana.createAccount();
498
+ console.log(`Created account: ${account.address}`);
499
+ return account;
500
+ }
501
+
502
+ async function requestFaucet(address: string) {
503
+ await cdp.solana.requestFaucet({
504
+ address,
505
+ token: "sol",
506
+ });
507
+ }
508
+
509
+ async function waitForBalance(address: string) {
510
+ let balance = 0;
511
+ let attempts = 0;
512
+ const maxAttempts = 30;
513
+
514
+ while (balance === 0 && attempts < maxAttempts) {
515
+ balance = await connection.getBalance(new PublicKey(address));
516
+ if (balance === 0) {
517
+ console.log("Waiting for funds...");
518
+ await new Promise(resolve => setTimeout(resolve, 1000));
519
+ attempts++;
520
+ } else {
521
+ console.log("Account funded with", balance / 1e9, "SOL");
522
+ }
523
+ }
524
+
525
+ if (balance === 0) {
526
+ throw new Error("Account not funded after multiple attempts");
527
+ }
528
+ }
529
+
530
+ async function sendTransaction(address: string) {
531
+ // Amount of lamports to send (default: 1000 = 0.000001 SOL)
532
+ const lamportsToSend = 1000;
533
+ const fromAddress = new PublicKey(address)
534
+ const toAddress = new PublicKey("EeVPcnRE1mhcY85wAh3uPJG1uFiTNya9dCJjNUPABXzo");
535
+
536
+ const { blockhash } = await connection.getLatestBlockhash();
537
+
538
+ const transaction = new Transaction();
539
+ transaction.add(
540
+ SystemProgram.transfer({
541
+ fromPubkey: fromAddress,
542
+ toPubkey: toAddress,
543
+ lamports: lamportsToSend,
544
+ })
545
+ );
546
+
547
+ transaction.recentBlockhash = blockhash;
548
+ transaction.feePayer = fromAddress;
549
+
550
+ const serializedTx = Buffer.from(
551
+ transaction.serialize({ requireAllSignatures: false })
552
+ ).toString("base64");
553
+
554
+ const { signature: txSignature } = await cdp.solana.signTransaction({
555
+ address,
556
+ transaction: serializedTx,
557
+ });
558
+ const decodedSignedTx = Buffer.from(txSignature, "base64");
559
+
560
+ console.log("Sending transaction...");
561
+ const txSendSignature = await connection.sendRawTransaction(decodedSignedTx);
562
+
563
+ const latestBlockhash = await connection.getLatestBlockhash();
564
+
565
+ console.log("Waiting for transaction to be confirmed...");
566
+ const confirmation = await connection.confirmTransaction({
567
+ signature: txSendSignature,
568
+ blockhash: latestBlockhash.blockhash,
569
+ lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
570
+ });
571
+
572
+ if (confirmation.value.err) {
573
+ throw new Error(`Transaction failed: ${confirmation.value.err.toString()}`);
574
+ }
575
+
576
+ console.log(`Sent SOL: https://explorer.solana.com/tx/${txSendSignature}?cluster=devnet`);
577
+ }
578
+
579
+ async function main() {
580
+ const account = await createAccount();
581
+ await requestFaucet(account.address);
582
+ await waitForBalance(account.address);
583
+ await sendTransaction(account.address);
584
+ }
585
+
586
+ main().catch(console.error)
587
+ ```
588
+ </Tab>
589
+
590
+ <Tab value="Python" title="Python">
591
+ You can send transactions on Solana using the [`solana`](https://solana.com/docs/clients/python) library.
592
+
593
+ ```bash
594
+ pip install solana solders
595
+ ```
596
+
597
+ In the example below, we:
598
+
599
+ 1. Create a new Solana account.
600
+ 2. Request SOL from the faucet.
601
+ 3. Wait for funds to become available.
602
+ 4. Send the transaction to a specified address.
603
+
604
+ ```python main.py lines wrap [expandable]
605
+ import time
606
+ import base64
607
+ import asyncio
608
+ from cdp import CdpClient
609
+ from dotenv import load_dotenv
610
+ from solana.rpc.api import Client as SolanaClient
611
+ from solana.rpc.types import TxOpts
612
+ from solders.pubkey import Pubkey as PublicKey
613
+ from solders.system_program import TransferParams, transfer
614
+ from solders.message import Message
615
+
616
+ load_dotenv()
617
+
618
+ cdp = CdpClient()
619
+
620
+ connection = SolanaClient("https://api.devnet.solana.com")
621
+
622
+ async def create_sol_account():
623
+ account = await cdp.solana.create_account()
624
+ print(f"Created account: {account.address}")
625
+ return account
626
+
627
+
628
+ async def request_faucet(address: str):
629
+ await cdp.solana.request_faucet(
630
+ address,
631
+ token="sol"
632
+ )
633
+
634
+
635
+ async def wait_for_balance(address: str):
636
+ balance = 0
637
+ max_attempts = 30
638
+ attempts = 0
639
+
640
+ while balance == 0 and attempts < max_attempts:
641
+ balance_resp = connection.get_balance(PublicKey.from_string(address))
642
+ balance = balance_resp.value
643
+ if balance == 0:
644
+ print("Waiting for funds...")
645
+ time.sleep(1)
646
+ attempts += 1
647
+ else:
648
+ print(f"Account funded with {balance / 1e9} SOL ({balance} lamports)")
649
+
650
+ if balance == 0:
651
+ raise ValueError("Account not funded after multiple attempts")
652
+
653
+
654
+ async def send_transaction(address: str):
655
+ # Amount of lamports to send (default: 1000 = 0.000001 SOL)
656
+ lamports_to_send = 1000;
657
+ from_address = PublicKey.from_string(address)
658
+ to_address = PublicKey.from_string("EeVPcnRE1mhcY85wAh3uPJG1uFiTNya9dCJjNUPABXzo")
659
+
660
+ blockhash_resp = connection.get_latest_blockhash()
661
+ blockhash = blockhash_resp.value.blockhash
662
+
663
+ transfer_params = TransferParams(
664
+ from_pubkey=from_address,
665
+ to_pubkey=to_address,
666
+ lamports=lamports_to_send,
667
+ )
668
+ transfer_instr = transfer(transfer_params)
669
+
670
+ message = Message.new_with_blockhash(
671
+ [transfer_instr],
672
+ from_address,
673
+ blockhash,
674
+ )
675
+
676
+ # Create a transaction envelope with signature space
677
+ sig_count = bytes([1]) # 1 byte for signature count (1)
678
+ empty_sig = bytes([0] * 64) # 64 bytes of zeros for the empty signature
679
+ message_bytes = bytes(message) # Get the serialized message bytes
680
+
681
+ # Concatenate to form the transaction bytes
682
+ tx_bytes = sig_count + empty_sig + message_bytes
683
+
684
+ # Encode to base64 used by CDP API
685
+ serialized_tx = base64.b64encode(tx_bytes).decode("utf-8")
686
+
687
+ signed_tx_response = await cdp.solana.sign_transaction(
688
+ address,
689
+ transaction=serialized_tx,
690
+ )
691
+
692
+ # Decode the signed transaction from base64
693
+ decoded_signed_tx = base64.b64decode(signed_tx_response.signed_transaction)
694
+
695
+ print("Sending transaction...")
696
+ tx_resp = connection.send_raw_transaction(
697
+ decoded_signed_tx,
698
+ opts=TxOpts(skip_preflight=False, preflight_commitment="processed"),
699
+ )
700
+ signature = tx_resp.value
701
+
702
+ print("Waiting for transaction to be confirmed...")
703
+ confirmation = connection.confirm_transaction(signature, commitment="processed")
704
+
705
+ if hasattr(confirmation, "err") and confirmation.err:
706
+ raise ValueError(f"Transaction failed: {confirmation.err}")
707
+
708
+ print(f"Sent SOL: https://explorer.solana.com/tx/{signature}?cluster=devnet")
709
+
710
+
711
+ async def main():
712
+ account = await create_sol_account()
713
+ await request_faucet(account.address)
714
+ await wait_for_balance(account.address)
715
+ await send_transaction(account.address)
716
+
717
+ await cdp.close()
718
+
719
+ asyncio.run(main())
720
+ ```
721
+ </Tab>
722
+ </Tabs>
723
+
724
+ ## Video: Watch and learn
725
+
726
+ Watch the video to learn about CDP Wallets and see a comprehensive demo, which covers:
727
+
728
+ * Overview of CDP Wallet API v2 features and capabilities
729
+ * Live demonstration of creating accounts and managing wallets
730
+ * Best practices for building with CDP Wallets
731
+
732
+ <Frame>
733
+ <iframe width="560" height="315" src="https://www.youtube.com/embed/_XMRgDU9a2Y" title="CDP Wallets Demo" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen />
734
+ </Frame>
735
+
736
+ ## What to read next
737
+
738
+ * [v2 Wallet Accounts](/wallet-api/v2/introduction/accounts): An overview of the types of accounts supported by the v2 Wallet API.
739
+ * [Using Smart Accounts](/wallet-api/v2/evm-features/smart-accounts): A step-by-step guide on how to create and use smart accounts.
740
+ * [v2 Wallet Security](/wallet-api/v2/introduction/security): Learn about the security features of the v2 Wallet API.
741
+ * [Faucets](/faucets/introduction/welcome): Learn more on supported testnet assets and their associated rate limits.