@coinbase/cdp-sdk 1.25.0 → 1.27.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.
- package/CHANGELOG.md +20 -0
- package/README.md +58 -2
- package/_cjs/accounts/evm/networkToChainResolver.js +68 -0
- package/_cjs/accounts/evm/networkToChainResolver.js.map +1 -0
- package/_cjs/accounts/evm/resolveViemClients.js +6 -32
- package/_cjs/accounts/evm/resolveViemClients.js.map +1 -1
- package/_cjs/accounts/evm/toEvmServerAccount.js +84 -0
- package/_cjs/accounts/evm/toEvmServerAccount.js.map +1 -1
- package/_cjs/accounts/evm/toEvmSmartAccount.js +93 -0
- package/_cjs/accounts/evm/toEvmSmartAccount.js.map +1 -1
- package/_cjs/accounts/evm/toNetworkScopedEvmServerAccount.js +19 -7
- package/_cjs/accounts/evm/toNetworkScopedEvmServerAccount.js.map +1 -1
- package/_cjs/accounts/evm/toNetworkScopedEvmSmartAccount.js +65 -0
- package/_cjs/accounts/evm/toNetworkScopedEvmSmartAccount.js.map +1 -1
- package/_cjs/accounts/solana/toSolanaAccount.js +20 -0
- package/_cjs/accounts/solana/toSolanaAccount.js.map +1 -1
- package/_cjs/actions/evm/fund/fund.js +3 -2
- package/_cjs/actions/evm/fund/fund.js.map +1 -1
- package/_cjs/actions/evm/fund/quoteFund.js +3 -2
- package/_cjs/actions/evm/fund/quoteFund.js.map +1 -1
- package/_cjs/actions/solana/sendTransaction.js +32 -0
- package/_cjs/actions/solana/sendTransaction.js.map +1 -0
- package/_cjs/analytics.js +39 -3
- package/_cjs/analytics.js.map +1 -1
- package/_cjs/auth/utils/jwt.js +6 -5
- package/_cjs/auth/utils/jwt.js.map +1 -1
- package/_cjs/client/cdp.js +4 -1
- package/_cjs/client/cdp.js.map +1 -1
- package/_cjs/client/evm/evm.js +116 -10
- package/_cjs/client/evm/evm.js.map +1 -1
- package/_cjs/client/policies/policies.js +22 -0
- package/_cjs/client/policies/policies.js.map +1 -1
- package/_cjs/client/solana/solana.js +85 -8
- package/_cjs/client/solana/solana.js.map +1 -1
- package/_cjs/constants.js +1 -1
- package/_cjs/constants.js.map +1 -1
- package/_cjs/errors.js +19 -1
- package/_cjs/errors.js.map +1 -1
- package/_cjs/index.js +3 -1
- package/_cjs/index.js.map +1 -1
- package/_cjs/openapi-client/cdpApiClient.js +46 -6
- package/_cjs/openapi-client/cdpApiClient.js.map +1 -1
- package/_cjs/openapi-client/errors.js +38 -9
- package/_cjs/openapi-client/errors.js.map +1 -1
- package/_cjs/openapi-client/generated/coinbaseDeveloperPlatformAPIs.schemas.js +6 -1
- package/_cjs/openapi-client/generated/coinbaseDeveloperPlatformAPIs.schemas.js.map +1 -1
- package/_cjs/openapi-client/generated/solana-accounts/solana-accounts.js +34 -1
- package/_cjs/openapi-client/generated/solana-accounts/solana-accounts.js.map +1 -1
- package/_cjs/openapi-client/generated/solana-accounts/solana-accounts.msw.js +18 -1
- package/_cjs/openapi-client/generated/solana-accounts/solana-accounts.msw.js.map +1 -1
- package/_cjs/version.js +1 -1
- package/_esm/accounts/evm/networkToChainResolver.js +31 -0
- package/_esm/accounts/evm/networkToChainResolver.js.map +1 -0
- package/_esm/accounts/evm/resolveViemClients.js +4 -30
- package/_esm/accounts/evm/resolveViemClients.js.map +1 -1
- package/_esm/accounts/evm/toEvmServerAccount.js +84 -0
- package/_esm/accounts/evm/toEvmServerAccount.js.map +1 -1
- package/_esm/accounts/evm/toEvmSmartAccount.js +93 -0
- package/_esm/accounts/evm/toEvmSmartAccount.js.map +1 -1
- package/_esm/accounts/evm/toNetworkScopedEvmServerAccount.js +19 -7
- package/_esm/accounts/evm/toNetworkScopedEvmServerAccount.js.map +1 -1
- package/_esm/accounts/evm/toNetworkScopedEvmSmartAccount.js +65 -0
- package/_esm/accounts/evm/toNetworkScopedEvmSmartAccount.js.map +1 -1
- package/_esm/accounts/solana/toSolanaAccount.js +20 -0
- package/_esm/accounts/solana/toSolanaAccount.js.map +1 -1
- package/_esm/actions/evm/fund/fund.js +3 -2
- package/_esm/actions/evm/fund/fund.js.map +1 -1
- package/_esm/actions/evm/fund/quoteFund.js +3 -2
- package/_esm/actions/evm/fund/quoteFund.js.map +1 -1
- package/_esm/actions/solana/sendTransaction.js +29 -0
- package/_esm/actions/solana/sendTransaction.js.map +1 -0
- package/_esm/analytics.js +39 -3
- package/_esm/analytics.js.map +1 -1
- package/_esm/auth/utils/jwt.js +3 -2
- package/_esm/auth/utils/jwt.js.map +1 -1
- package/_esm/client/cdp.js +4 -1
- package/_esm/client/cdp.js.map +1 -1
- package/_esm/client/evm/evm.js +110 -4
- package/_esm/client/evm/evm.js.map +1 -1
- package/_esm/client/policies/policies.js +22 -0
- package/_esm/client/policies/policies.js.map +1 -1
- package/_esm/client/solana/solana.js +82 -5
- package/_esm/client/solana/solana.js.map +1 -1
- package/_esm/constants.js +1 -1
- package/_esm/constants.js.map +1 -1
- package/_esm/errors.js +17 -0
- package/_esm/errors.js.map +1 -1
- package/_esm/index.js +1 -0
- package/_esm/index.js.map +1 -1
- package/_esm/openapi-client/cdpApiClient.js +47 -7
- package/_esm/openapi-client/cdpApiClient.js.map +1 -1
- package/_esm/openapi-client/errors.js +36 -8
- package/_esm/openapi-client/errors.js.map +1 -1
- package/_esm/openapi-client/generated/coinbaseDeveloperPlatformAPIs.schemas.js +5 -0
- package/_esm/openapi-client/generated/coinbaseDeveloperPlatformAPIs.schemas.js.map +1 -1
- package/_esm/openapi-client/generated/solana-accounts/solana-accounts.js +32 -0
- package/_esm/openapi-client/generated/solana-accounts/solana-accounts.js.map +1 -1
- package/_esm/openapi-client/generated/solana-accounts/solana-accounts.msw.js +15 -0
- package/_esm/openapi-client/generated/solana-accounts/solana-accounts.msw.js.map +1 -1
- package/_esm/version.js +1 -1
- package/_types/accounts/evm/networkToChainResolver.d.ts +14 -0
- package/_types/accounts/evm/networkToChainResolver.d.ts.map +1 -0
- package/_types/accounts/evm/resolveViemClients.d.ts.map +1 -1
- package/_types/accounts/evm/toEvmServerAccount.d.ts.map +1 -1
- package/_types/accounts/evm/toEvmSmartAccount.d.ts.map +1 -1
- package/_types/accounts/evm/toNetworkScopedEvmServerAccount.d.ts.map +1 -1
- package/_types/accounts/evm/toNetworkScopedEvmSmartAccount.d.ts.map +1 -1
- package/_types/accounts/evm/types.d.ts +3 -3
- package/_types/accounts/evm/types.d.ts.map +1 -1
- package/_types/accounts/solana/toSolanaAccount.d.ts.map +1 -1
- package/_types/actions/evm/fund/fund.d.ts.map +1 -1
- package/_types/actions/evm/fund/quoteFund.d.ts.map +1 -1
- package/_types/actions/evm/types.d.ts +36 -3
- package/_types/actions/evm/types.d.ts.map +1 -1
- package/_types/actions/solana/sendTransaction.d.ts +23 -0
- package/_types/actions/solana/sendTransaction.d.ts.map +1 -0
- package/_types/analytics.d.ts +36 -1
- package/_types/analytics.d.ts.map +1 -1
- package/_types/auth/utils/jwt.d.ts.map +1 -1
- package/_types/client/cdp.d.ts.map +1 -1
- package/_types/client/evm/evm.d.ts +1 -0
- package/_types/client/evm/evm.d.ts.map +1 -1
- package/_types/client/policies/policies.d.ts.map +1 -1
- package/_types/client/solana/solana.d.ts +21 -2
- package/_types/client/solana/solana.d.ts.map +1 -1
- package/_types/client/solana/solana.types.d.ts +22 -3
- package/_types/client/solana/solana.types.d.ts.map +1 -1
- package/_types/constants.d.ts +1 -1
- package/_types/constants.d.ts.map +1 -1
- package/_types/errors.d.ts +11 -0
- package/_types/errors.d.ts.map +1 -1
- package/_types/index.d.ts +1 -0
- package/_types/index.d.ts.map +1 -1
- package/_types/openapi-client/cdpApiClient.d.ts.map +1 -1
- package/_types/openapi-client/errors.d.ts +46 -9
- package/_types/openapi-client/errors.d.ts.map +1 -1
- package/_types/openapi-client/generated/coinbaseDeveloperPlatformAPIs.schemas.d.ts +18 -0
- package/_types/openapi-client/generated/coinbaseDeveloperPlatformAPIs.schemas.d.ts.map +1 -1
- package/_types/openapi-client/generated/solana-accounts/solana-accounts.d.ts +27 -1
- package/_types/openapi-client/generated/solana-accounts/solana-accounts.d.ts.map +1 -1
- package/_types/openapi-client/generated/solana-accounts/solana-accounts.msw.d.ts +3 -1
- package/_types/openapi-client/generated/solana-accounts/solana-accounts.msw.d.ts.map +1 -1
- package/_types/openapi-client/index.d.ts +2 -0
- package/_types/openapi-client/index.d.ts.map +1 -1
- package/_types/version.d.ts +1 -1
- package/accounts/evm/networkToChainResolver.ts +34 -0
- package/accounts/evm/resolveViemClients.ts +6 -32
- package/accounts/evm/toEvmServerAccount.ts +98 -0
- package/accounts/evm/toEvmSmartAccount.ts +114 -2
- package/accounts/evm/toNetworkScopedEvmServerAccount.ts +22 -8
- package/accounts/evm/toNetworkScopedEvmSmartAccount.ts +75 -0
- package/accounts/evm/types.ts +3 -3
- package/accounts/solana/toSolanaAccount.ts +24 -0
- package/actions/evm/fund/fund.ts +3 -2
- package/actions/evm/fund/quoteFund.ts +3 -2
- package/actions/evm/types.ts +41 -2
- package/actions/solana/sendTransaction.ts +38 -0
- package/analytics.ts +73 -4
- package/auth/utils/jwt.ts +5 -2
- package/client/cdp.ts +7 -1
- package/client/evm/evm.ts +135 -6
- package/client/policies/policies.ts +28 -0
- package/client/solana/solana.ts +97 -5
- package/client/solana/solana.types.ts +25 -1
- package/constants.ts +1 -1
- package/errors.ts +18 -0
- package/index.ts +1 -0
- package/openapi-client/cdpApiClient.ts +87 -17
- package/openapi-client/errors.ts +65 -10
- package/openapi-client/generated/coinbaseDeveloperPlatformAPIs.schemas.ts +24 -0
- package/openapi-client/generated/solana-accounts/solana-accounts.msw.ts +32 -0
- package/openapi-client/generated/solana-accounts/solana-accounts.ts +43 -0
- package/package.json +1 -1
- package/version.ts +1 -1
package/client/solana/solana.ts
CHANGED
|
@@ -13,19 +13,23 @@ import {
|
|
|
13
13
|
ListTokenBalancesOptions,
|
|
14
14
|
ListTokenBalancesResult,
|
|
15
15
|
RequestFaucetOptions,
|
|
16
|
+
SendTransactionOptions,
|
|
16
17
|
SignatureResult,
|
|
17
18
|
SignMessageOptions,
|
|
18
19
|
SignTransactionOptions,
|
|
19
20
|
SolanaClientInterface,
|
|
21
|
+
TransactionResult,
|
|
20
22
|
UpdateSolanaAccountOptions,
|
|
21
23
|
} from "./solana.types.js";
|
|
22
24
|
import { toSolanaAccount } from "../../accounts/solana/toSolanaAccount.js";
|
|
23
25
|
import { SolanaAccount } from "../../accounts/solana/types.js";
|
|
24
26
|
import { requestFaucet } from "../../actions/solana/requestFaucet.js";
|
|
27
|
+
import { sendTransaction } from "../../actions/solana/sendTransaction.js";
|
|
25
28
|
import { signMessage } from "../../actions/solana/signMessage.js";
|
|
26
29
|
import { signTransaction } from "../../actions/solana/signTransaction.js";
|
|
27
30
|
import { Analytics } from "../../analytics.js";
|
|
28
31
|
import { ImportAccountPublicRSAKey } from "../../constants.js";
|
|
32
|
+
import { UserInputValidationError } from "../../errors.js";
|
|
29
33
|
import { APIError } from "../../openapi-client/errors.js";
|
|
30
34
|
import { CdpOpenApiClient } from "../../openapi-client/index.js";
|
|
31
35
|
import {
|
|
@@ -69,6 +73,11 @@ export class SolanaClient implements SolanaClientInterface {
|
|
|
69
73
|
* ```
|
|
70
74
|
*/
|
|
71
75
|
async createAccount(options: CreateAccountOptions = {}): Promise<SolanaAccount> {
|
|
76
|
+
Analytics.trackAction({
|
|
77
|
+
action: "create_account",
|
|
78
|
+
accountType: "solana",
|
|
79
|
+
});
|
|
80
|
+
|
|
72
81
|
const openApiAccount = await CdpOpenApiClient.createSolanaAccount(
|
|
73
82
|
{
|
|
74
83
|
name: options.name,
|
|
@@ -111,6 +120,11 @@ export class SolanaClient implements SolanaClientInterface {
|
|
|
111
120
|
* ```
|
|
112
121
|
*/
|
|
113
122
|
async exportAccount(options: ExportAccountOptions): Promise<string> {
|
|
123
|
+
Analytics.trackAction({
|
|
124
|
+
action: "export_account",
|
|
125
|
+
accountType: "solana",
|
|
126
|
+
});
|
|
127
|
+
|
|
114
128
|
const { publicKey, privateKey } = await generateExportEncryptionKeyPair();
|
|
115
129
|
|
|
116
130
|
const { encryptedPrivateKey } = await (async () => {
|
|
@@ -134,7 +148,7 @@ export class SolanaClient implements SolanaClientInterface {
|
|
|
134
148
|
);
|
|
135
149
|
}
|
|
136
150
|
|
|
137
|
-
throw new
|
|
151
|
+
throw new UserInputValidationError("Either address or name must be provided");
|
|
138
152
|
})();
|
|
139
153
|
|
|
140
154
|
const decryptedPrivateKey = decryptWithPrivateKey(privateKey, encryptedPrivateKey);
|
|
@@ -180,6 +194,11 @@ export class SolanaClient implements SolanaClientInterface {
|
|
|
180
194
|
* ```
|
|
181
195
|
*/
|
|
182
196
|
async importAccount(options: ImportAccountOptions): Promise<SolanaAccount> {
|
|
197
|
+
Analytics.trackAction({
|
|
198
|
+
action: "import_account",
|
|
199
|
+
accountType: "solana",
|
|
200
|
+
});
|
|
201
|
+
|
|
183
202
|
let privateKeyBytes: Uint8Array = new Uint8Array();
|
|
184
203
|
|
|
185
204
|
if (typeof options.privateKey === "string") {
|
|
@@ -189,7 +208,7 @@ export class SolanaClient implements SolanaClientInterface {
|
|
|
189
208
|
}
|
|
190
209
|
|
|
191
210
|
if (privateKeyBytes.length !== 32 && privateKeyBytes.length !== 64) {
|
|
192
|
-
throw new
|
|
211
|
+
throw new UserInputValidationError("Invalid private key length");
|
|
193
212
|
}
|
|
194
213
|
|
|
195
214
|
if (privateKeyBytes.length === 64) {
|
|
@@ -250,6 +269,11 @@ export class SolanaClient implements SolanaClientInterface {
|
|
|
250
269
|
* ```
|
|
251
270
|
*/
|
|
252
271
|
async getAccount(options: GetAccountOptions): Promise<SolanaAccount> {
|
|
272
|
+
Analytics.trackAction({
|
|
273
|
+
action: "get_account",
|
|
274
|
+
accountType: "solana",
|
|
275
|
+
});
|
|
276
|
+
|
|
253
277
|
const openApiAccount = await (() => {
|
|
254
278
|
if (options.address) {
|
|
255
279
|
return CdpOpenApiClient.getSolanaAccount(options.address);
|
|
@@ -259,7 +283,7 @@ export class SolanaClient implements SolanaClientInterface {
|
|
|
259
283
|
return CdpOpenApiClient.getSolanaAccountByName(options.name);
|
|
260
284
|
}
|
|
261
285
|
|
|
262
|
-
throw new
|
|
286
|
+
throw new UserInputValidationError("Either address or name must be provided");
|
|
263
287
|
})();
|
|
264
288
|
|
|
265
289
|
const account = toSolanaAccount(CdpOpenApiClient, {
|
|
@@ -287,6 +311,11 @@ export class SolanaClient implements SolanaClientInterface {
|
|
|
287
311
|
* ```
|
|
288
312
|
*/
|
|
289
313
|
async getOrCreateAccount(options: GetOrCreateAccountOptions): Promise<SolanaAccount> {
|
|
314
|
+
Analytics.trackAction({
|
|
315
|
+
action: "get_or_create_account",
|
|
316
|
+
accountType: "solana",
|
|
317
|
+
});
|
|
318
|
+
|
|
290
319
|
try {
|
|
291
320
|
const account = await this.getAccount(options);
|
|
292
321
|
return account;
|
|
@@ -341,6 +370,11 @@ export class SolanaClient implements SolanaClientInterface {
|
|
|
341
370
|
* ```
|
|
342
371
|
*/
|
|
343
372
|
async listAccounts(options: ListAccountsOptions = {}): Promise<ListAccountsResult> {
|
|
373
|
+
Analytics.trackAction({
|
|
374
|
+
action: "list_accounts",
|
|
375
|
+
accountType: "solana",
|
|
376
|
+
});
|
|
377
|
+
|
|
344
378
|
const solAccounts = await CdpOpenApiClient.listSolanaAccounts({
|
|
345
379
|
pageSize: options.pageSize,
|
|
346
380
|
pageToken: options.pageToken,
|
|
@@ -379,6 +413,11 @@ export class SolanaClient implements SolanaClientInterface {
|
|
|
379
413
|
* ```
|
|
380
414
|
*/
|
|
381
415
|
async requestFaucet(options: RequestFaucetOptions): Promise<SignatureResult> {
|
|
416
|
+
Analytics.trackAction({
|
|
417
|
+
action: "request_faucet",
|
|
418
|
+
accountType: "solana",
|
|
419
|
+
});
|
|
420
|
+
|
|
382
421
|
return requestFaucet(CdpOpenApiClient, options);
|
|
383
422
|
}
|
|
384
423
|
|
|
@@ -405,6 +444,11 @@ export class SolanaClient implements SolanaClientInterface {
|
|
|
405
444
|
* ```
|
|
406
445
|
*/
|
|
407
446
|
async signMessage(options: SignMessageOptions): Promise<SignatureResult> {
|
|
447
|
+
Analytics.trackAction({
|
|
448
|
+
action: "sign_message",
|
|
449
|
+
accountType: "solana",
|
|
450
|
+
});
|
|
451
|
+
|
|
408
452
|
return signMessage(CdpOpenApiClient, options);
|
|
409
453
|
}
|
|
410
454
|
|
|
@@ -442,6 +486,11 @@ export class SolanaClient implements SolanaClientInterface {
|
|
|
442
486
|
* ```
|
|
443
487
|
*/
|
|
444
488
|
async signTransaction(options: SignTransactionOptions): Promise<SignatureResult> {
|
|
489
|
+
Analytics.trackAction({
|
|
490
|
+
action: "sign_transaction",
|
|
491
|
+
accountType: "solana",
|
|
492
|
+
});
|
|
493
|
+
|
|
445
494
|
return signTransaction(CdpOpenApiClient, options);
|
|
446
495
|
}
|
|
447
496
|
|
|
@@ -487,6 +536,11 @@ export class SolanaClient implements SolanaClientInterface {
|
|
|
487
536
|
* ```
|
|
488
537
|
*/
|
|
489
538
|
async updateAccount(options: UpdateSolanaAccountOptions): Promise<SolanaAccount> {
|
|
539
|
+
Analytics.trackAction({
|
|
540
|
+
action: "update_account",
|
|
541
|
+
accountType: "solana",
|
|
542
|
+
});
|
|
543
|
+
|
|
490
544
|
const openApiAccount = await CdpOpenApiClient.updateSolanaAccount(
|
|
491
545
|
options.address,
|
|
492
546
|
options.update,
|
|
@@ -502,12 +556,42 @@ export class SolanaClient implements SolanaClientInterface {
|
|
|
502
556
|
return account;
|
|
503
557
|
}
|
|
504
558
|
|
|
559
|
+
/**
|
|
560
|
+
* Sends a Solana transaction using the Coinbase API.
|
|
561
|
+
*
|
|
562
|
+
* @param {SendTransactionOptions} options - Parameters for sending the Solana transaction.
|
|
563
|
+
* @param {string} options.network - The network to send the transaction to.
|
|
564
|
+
* @param {string} options.transaction - The base64 encoded transaction to send.
|
|
565
|
+
* @param {string} [options.idempotencyKey] - An idempotency key.
|
|
566
|
+
*
|
|
567
|
+
* @returns A promise that resolves to the transaction result.
|
|
568
|
+
*
|
|
569
|
+
* @example
|
|
570
|
+
* ```ts
|
|
571
|
+
* const signature = await cdp.solana.sendTransaction({
|
|
572
|
+
* network: "solana-devnet",
|
|
573
|
+
* transaction: "...",
|
|
574
|
+
* });
|
|
575
|
+
* ```
|
|
576
|
+
*/
|
|
577
|
+
async sendTransaction(options: SendTransactionOptions): Promise<TransactionResult> {
|
|
578
|
+
Analytics.trackAction({
|
|
579
|
+
action: "send_transaction",
|
|
580
|
+
accountType: "solana",
|
|
581
|
+
properties: {
|
|
582
|
+
network: options.network,
|
|
583
|
+
},
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
return sendTransaction(CdpOpenApiClient, options);
|
|
587
|
+
}
|
|
588
|
+
|
|
505
589
|
/**
|
|
506
590
|
* Lists the token balances for a Solana account.
|
|
507
591
|
*
|
|
508
592
|
* @param {ListTokenBalancesOptions} options - Parameters for listing the Solana token balances.
|
|
509
593
|
* @param {string} options.address - The address of the account to list token balances for.
|
|
510
|
-
* @param {string} options.network - The network to list token balances for.
|
|
594
|
+
* @param {string} [options.network] - The network to list token balances for. Defaults to "solana".
|
|
511
595
|
* @param {number} [options.pageSize] - The number of token balances to return.
|
|
512
596
|
* @param {string} [options.pageToken] - The page token to begin listing from.
|
|
513
597
|
* This is obtained by previous calls to this method.
|
|
@@ -520,8 +604,16 @@ export class SolanaClient implements SolanaClientInterface {
|
|
|
520
604
|
* ```
|
|
521
605
|
*/
|
|
522
606
|
async listTokenBalances(options: ListTokenBalancesOptions): Promise<ListTokenBalancesResult> {
|
|
607
|
+
Analytics.trackAction({
|
|
608
|
+
action: "list_token_balances",
|
|
609
|
+
accountType: "solana",
|
|
610
|
+
properties: {
|
|
611
|
+
network: options.network,
|
|
612
|
+
},
|
|
613
|
+
});
|
|
614
|
+
|
|
523
615
|
const tokenBalances = await CdpOpenApiClient.listSolanaTokenBalances(
|
|
524
|
-
options.network,
|
|
616
|
+
options.network || "solana",
|
|
525
617
|
options.address,
|
|
526
618
|
{
|
|
527
619
|
pageSize: options.pageSize,
|
|
@@ -2,6 +2,8 @@ import { Account, SolanaAccount } from "../../accounts/solana/types.js";
|
|
|
2
2
|
import {
|
|
3
3
|
ListSolanaTokenBalancesNetwork,
|
|
4
4
|
OpenApiSolanaMethods,
|
|
5
|
+
SendSolanaTransactionBody,
|
|
6
|
+
SendSolanaTransactionBodyNetwork,
|
|
5
7
|
UpdateSolanaAccountBody,
|
|
6
8
|
} from "../../openapi-client/index.js";
|
|
7
9
|
|
|
@@ -23,6 +25,7 @@ export type SolanaClientInterface = Omit<
|
|
|
23
25
|
| "exportSolanaAccountByName" // mapped to exportAccount
|
|
24
26
|
| "importSolanaAccount" // mapped to importAccount
|
|
25
27
|
| "listSolanaTokenBalances" // mapped to listTokenBalances
|
|
28
|
+
| "sendSolanaTransaction" // mapped to sendTransaction
|
|
26
29
|
> & {
|
|
27
30
|
createAccount: (options: CreateAccountOptions) => Promise<Account>;
|
|
28
31
|
exportAccount: (options: ExportAccountOptions) => Promise<string>;
|
|
@@ -35,6 +38,7 @@ export type SolanaClientInterface = Omit<
|
|
|
35
38
|
signMessage: (options: SignMessageOptions) => Promise<SignatureResult>;
|
|
36
39
|
signTransaction: (options: SignTransactionOptions) => Promise<SignatureResult>;
|
|
37
40
|
listTokenBalances: (options: ListTokenBalancesOptions) => Promise<ListTokenBalancesResult>;
|
|
41
|
+
sendTransaction: (options: SendSolanaTransactionBody) => Promise<SignatureResult>;
|
|
38
42
|
};
|
|
39
43
|
|
|
40
44
|
/**
|
|
@@ -178,13 +182,33 @@ export interface ListTokenBalancesOptions {
|
|
|
178
182
|
/** The address of the account. */
|
|
179
183
|
address: string;
|
|
180
184
|
/** The network to list token balances for. */
|
|
181
|
-
network
|
|
185
|
+
network?: ListSolanaTokenBalancesNetwork;
|
|
182
186
|
/** The page size. */
|
|
183
187
|
pageSize?: number;
|
|
184
188
|
/** The page token. */
|
|
185
189
|
pageToken?: string;
|
|
186
190
|
}
|
|
187
191
|
|
|
192
|
+
/**
|
|
193
|
+
* Options for sending a Solana transaction.
|
|
194
|
+
*/
|
|
195
|
+
export interface SendTransactionOptions {
|
|
196
|
+
/** The network to send the transaction to. */
|
|
197
|
+
network: SendSolanaTransactionBodyNetwork;
|
|
198
|
+
/** The base64 encoded transaction to send. */
|
|
199
|
+
transaction: string;
|
|
200
|
+
/** The idempotency key. */
|
|
201
|
+
idempotencyKey?: string;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* The result of sending a Solana transaction.
|
|
206
|
+
*/
|
|
207
|
+
export interface TransactionResult {
|
|
208
|
+
/** The signature of the transaction base58 encoded. */
|
|
209
|
+
signature: string;
|
|
210
|
+
}
|
|
211
|
+
|
|
188
212
|
export interface SolanaTokenAmount {
|
|
189
213
|
/** The amount of the token. */
|
|
190
214
|
amount: bigint;
|
package/constants.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The URL to the Coinbase CDP API error docs page.
|
|
3
3
|
*/
|
|
4
|
-
export const ERROR_DOCS_PAGE_URL = "https://docs.cdp.coinbase.com/api-v2/
|
|
4
|
+
export const ERROR_DOCS_PAGE_URL = "https://docs.cdp.coinbase.com/api-reference/v2/errors";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* The public RSA key used to encrypt the private key when importing an EVM or Solana account.
|
package/errors.ts
CHANGED
|
@@ -15,3 +15,21 @@ export class TimeoutError extends Error {
|
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* UserInputValidationError is thrown when validation of a user-supplied input fails.
|
|
21
|
+
*/
|
|
22
|
+
export class UserInputValidationError extends Error {
|
|
23
|
+
/**
|
|
24
|
+
* Initializes a new UserInputValidationError instance.
|
|
25
|
+
*
|
|
26
|
+
* @param message - The user input validation error message.
|
|
27
|
+
*/
|
|
28
|
+
constructor(message: string) {
|
|
29
|
+
super(message);
|
|
30
|
+
this.name = "UserInputValidationError";
|
|
31
|
+
if (Error.captureStackTrace) {
|
|
32
|
+
Error.captureStackTrace(this, UserInputValidationError);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
package/index.ts
CHANGED
|
@@ -3,13 +3,7 @@ import Axios, { AxiosInstance, AxiosRequestConfig } from "axios";
|
|
|
3
3
|
|
|
4
4
|
import { withAuth } from "../auth/hooks/axios/index.js";
|
|
5
5
|
import { ERROR_DOCS_PAGE_URL } from "../constants.js";
|
|
6
|
-
import {
|
|
7
|
-
isOpenAPIError,
|
|
8
|
-
APIError,
|
|
9
|
-
HttpErrorType,
|
|
10
|
-
UnknownApiError,
|
|
11
|
-
UnknownError,
|
|
12
|
-
} from "./errors.js";
|
|
6
|
+
import { isOpenAPIError, APIError, UnknownError, NetworkError } from "./errors.js";
|
|
13
7
|
|
|
14
8
|
import type { Prettify } from "../types/utils.js";
|
|
15
9
|
|
|
@@ -135,11 +129,52 @@ export const cdpApiClient = async <T>(
|
|
|
135
129
|
} catch (error) {
|
|
136
130
|
// eslint-disable-next-line import/no-named-as-default-member
|
|
137
131
|
if (Axios.isAxiosError(error) && !error.response) {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
132
|
+
// Network-level errors (no response received)
|
|
133
|
+
const errorMessage = (error.message || "").toLowerCase();
|
|
134
|
+
const errorCode = error.code?.toLowerCase();
|
|
135
|
+
|
|
136
|
+
// Categorize network errors based on error messages and codes
|
|
137
|
+
if (errorCode === "econnrefused" || errorMessage.includes("connection refused")) {
|
|
138
|
+
throw new NetworkError(
|
|
139
|
+
"network_connection_failed",
|
|
140
|
+
"Unable to connect to CDP service. The service may be unavailable.",
|
|
141
|
+
{ code: error.code, message: error.message, retryable: true },
|
|
142
|
+
error.cause,
|
|
143
|
+
);
|
|
144
|
+
} else if (
|
|
145
|
+
errorCode === "etimedout" ||
|
|
146
|
+
errorCode === "econnaborted" ||
|
|
147
|
+
errorMessage.includes("timeout")
|
|
148
|
+
) {
|
|
149
|
+
throw new NetworkError(
|
|
150
|
+
"network_timeout",
|
|
151
|
+
"Request timed out. Please try again.",
|
|
152
|
+
{ code: error.code, message: error.message, retryable: true },
|
|
153
|
+
error.cause,
|
|
154
|
+
);
|
|
155
|
+
} else if (errorCode === "enotfound" || errorMessage.includes("getaddrinfo")) {
|
|
156
|
+
throw new NetworkError(
|
|
157
|
+
"network_dns_failure",
|
|
158
|
+
"DNS resolution failed. Please check your network connection.",
|
|
159
|
+
{ code: error.code, message: error.message, retryable: false },
|
|
160
|
+
error.cause,
|
|
161
|
+
);
|
|
162
|
+
} else if (errorMessage.includes("network error") || errorMessage.includes("econnreset")) {
|
|
163
|
+
throw new NetworkError(
|
|
164
|
+
"network_connection_failed",
|
|
165
|
+
"Network error occurred. Please check your connection and try again.",
|
|
166
|
+
{ code: error.code, message: error.message, retryable: true },
|
|
167
|
+
error.cause,
|
|
168
|
+
);
|
|
169
|
+
} else {
|
|
170
|
+
// Generic network error
|
|
171
|
+
throw new NetworkError(
|
|
172
|
+
"unknown",
|
|
173
|
+
error.cause instanceof Error ? error.cause.message : error.message,
|
|
174
|
+
{ code: error.code, message: error.message, retryable: true },
|
|
175
|
+
error.cause,
|
|
176
|
+
);
|
|
177
|
+
}
|
|
143
178
|
}
|
|
144
179
|
|
|
145
180
|
// eslint-disable-next-line import/no-named-as-default-member
|
|
@@ -155,20 +190,55 @@ export const cdpApiClient = async <T>(
|
|
|
155
190
|
);
|
|
156
191
|
} else {
|
|
157
192
|
const statusCode = error.response.status;
|
|
193
|
+
const responseData = error.response.data;
|
|
194
|
+
|
|
195
|
+
// Check for gateway-level errors that might indicate network issues
|
|
196
|
+
const isGatewayError =
|
|
197
|
+
responseData &&
|
|
198
|
+
typeof responseData === "string" &&
|
|
199
|
+
(responseData.toLowerCase().includes("forbidden") ||
|
|
200
|
+
responseData.toLowerCase().includes("ip") ||
|
|
201
|
+
responseData.toLowerCase().includes("blocked") ||
|
|
202
|
+
responseData.toLowerCase().includes("gateway"));
|
|
203
|
+
|
|
158
204
|
switch (statusCode) {
|
|
159
205
|
case 401:
|
|
160
206
|
throw new APIError(
|
|
161
207
|
statusCode,
|
|
162
|
-
|
|
208
|
+
"unauthorized",
|
|
163
209
|
"Unauthorized.",
|
|
164
210
|
undefined,
|
|
165
211
|
`${ERROR_DOCS_PAGE_URL}#unauthorized`,
|
|
166
212
|
error.cause,
|
|
167
213
|
);
|
|
214
|
+
case 403:
|
|
215
|
+
// Special handling for IP blocklist and other gateway-level 403s
|
|
216
|
+
if (isGatewayError) {
|
|
217
|
+
throw new NetworkError(
|
|
218
|
+
"network_ip_blocked",
|
|
219
|
+
"Access denied. Your IP address may be blocked or restricted.",
|
|
220
|
+
{
|
|
221
|
+
code: "IP_BLOCKED",
|
|
222
|
+
message:
|
|
223
|
+
typeof responseData === "string" ? responseData : JSON.stringify(responseData),
|
|
224
|
+
retryable: false,
|
|
225
|
+
},
|
|
226
|
+
error.cause,
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
// Regular 403 forbidden error
|
|
230
|
+
throw new APIError(
|
|
231
|
+
statusCode,
|
|
232
|
+
"unauthorized",
|
|
233
|
+
"Forbidden. You don't have permission to access this resource.",
|
|
234
|
+
undefined,
|
|
235
|
+
`${ERROR_DOCS_PAGE_URL}#forbidden`,
|
|
236
|
+
error.cause,
|
|
237
|
+
);
|
|
168
238
|
case 404:
|
|
169
239
|
throw new APIError(
|
|
170
240
|
statusCode,
|
|
171
|
-
|
|
241
|
+
"not_found",
|
|
172
242
|
"API not found.",
|
|
173
243
|
undefined,
|
|
174
244
|
`${ERROR_DOCS_PAGE_URL}#not_found`,
|
|
@@ -177,7 +247,7 @@ export const cdpApiClient = async <T>(
|
|
|
177
247
|
case 502:
|
|
178
248
|
throw new APIError(
|
|
179
249
|
statusCode,
|
|
180
|
-
|
|
250
|
+
"bad_gateway",
|
|
181
251
|
"Bad gateway.",
|
|
182
252
|
undefined,
|
|
183
253
|
`${ERROR_DOCS_PAGE_URL}`,
|
|
@@ -186,7 +256,7 @@ export const cdpApiClient = async <T>(
|
|
|
186
256
|
case 503:
|
|
187
257
|
throw new APIError(
|
|
188
258
|
statusCode,
|
|
189
|
-
|
|
259
|
+
"service_unavailable",
|
|
190
260
|
"Service unavailable. Please try again later.",
|
|
191
261
|
undefined,
|
|
192
262
|
`${ERROR_DOCS_PAGE_URL}`,
|
|
@@ -209,7 +279,7 @@ export const cdpApiClient = async <T>(
|
|
|
209
279
|
|
|
210
280
|
throw new APIError(
|
|
211
281
|
statusCode,
|
|
212
|
-
|
|
282
|
+
"unexpected_error",
|
|
213
283
|
errorMessage,
|
|
214
284
|
undefined,
|
|
215
285
|
`${ERROR_DOCS_PAGE_URL}`,
|
package/openapi-client/errors.ts
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
|
+
import { ERROR_DOCS_PAGE_URL } from "../constants.js";
|
|
1
2
|
import {
|
|
2
3
|
Error as OpenAPIError,
|
|
3
4
|
ErrorType as OpenAPIErrorType,
|
|
4
5
|
} from "./generated/coinbaseDeveloperPlatformAPIs.schemas.js";
|
|
5
6
|
|
|
6
|
-
export
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
7
|
+
export type HttpErrorType =
|
|
8
|
+
| "unexpected_error"
|
|
9
|
+
| "unauthorized"
|
|
10
|
+
| "not_found"
|
|
11
|
+
| "bad_gateway"
|
|
12
|
+
| "service_unavailable"
|
|
13
|
+
| "unknown"
|
|
14
|
+
| "network_timeout"
|
|
15
|
+
| "network_connection_failed"
|
|
16
|
+
| "network_ip_blocked"
|
|
17
|
+
| "network_dns_failure";
|
|
16
18
|
|
|
17
19
|
/**
|
|
18
20
|
* Extended error codes that include both OpenAPI errors and network errors
|
|
@@ -114,6 +116,59 @@ export class UnknownError extends Error {
|
|
|
114
116
|
}
|
|
115
117
|
}
|
|
116
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Error thrown when a network-level failure occurs before reaching the CDP service
|
|
121
|
+
* This includes gateway errors, IP blocklist rejections, DNS failures, etc.
|
|
122
|
+
*/
|
|
123
|
+
export class NetworkError extends APIError {
|
|
124
|
+
networkDetails?: {
|
|
125
|
+
code?: string;
|
|
126
|
+
message?: string;
|
|
127
|
+
retryable?: boolean;
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Constructor for the NetworkError class
|
|
132
|
+
*
|
|
133
|
+
* @param errorType - The type of network error
|
|
134
|
+
* @param errorMessage - The error message
|
|
135
|
+
* @param networkDetails - Additional network error details
|
|
136
|
+
* @param networkDetails.code - The error code
|
|
137
|
+
* @param networkDetails.message - The error message
|
|
138
|
+
* @param networkDetails.retryable - Whether the error is retryable
|
|
139
|
+
* @param cause - The cause of the error
|
|
140
|
+
*/
|
|
141
|
+
constructor(
|
|
142
|
+
errorType: HttpErrorType,
|
|
143
|
+
errorMessage: string,
|
|
144
|
+
networkDetails?: { code?: string; message?: string; retryable?: boolean },
|
|
145
|
+
cause?: Error,
|
|
146
|
+
) {
|
|
147
|
+
super(
|
|
148
|
+
0, // Status code 0 indicates no response was received
|
|
149
|
+
errorType,
|
|
150
|
+
errorMessage,
|
|
151
|
+
undefined,
|
|
152
|
+
`${ERROR_DOCS_PAGE_URL}#network-errors`,
|
|
153
|
+
cause,
|
|
154
|
+
);
|
|
155
|
+
this.name = "NetworkError";
|
|
156
|
+
this.networkDetails = networkDetails;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Convert the error to a JSON object, including network details
|
|
161
|
+
*
|
|
162
|
+
* @returns The error as a JSON object
|
|
163
|
+
*/
|
|
164
|
+
toJSON() {
|
|
165
|
+
return {
|
|
166
|
+
...super.toJSON(),
|
|
167
|
+
...(this.networkDetails && { networkDetails: this.networkDetails }),
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
117
172
|
/**
|
|
118
173
|
* Type guard to check if an object is an OpenAPIError
|
|
119
174
|
*
|
|
@@ -2252,6 +2252,30 @@ export type SignSolanaMessage200 = {
|
|
|
2252
2252
|
signature: string;
|
|
2253
2253
|
};
|
|
2254
2254
|
|
|
2255
|
+
/**
|
|
2256
|
+
* The Solana network to send the transaction to.
|
|
2257
|
+
*/
|
|
2258
|
+
export type SendSolanaTransactionBodyNetwork =
|
|
2259
|
+
(typeof SendSolanaTransactionBodyNetwork)[keyof typeof SendSolanaTransactionBodyNetwork];
|
|
2260
|
+
|
|
2261
|
+
// eslint-disable-next-line @typescript-eslint/no-redeclare
|
|
2262
|
+
export const SendSolanaTransactionBodyNetwork = {
|
|
2263
|
+
solana: "solana",
|
|
2264
|
+
"solana-devnet": "solana-devnet",
|
|
2265
|
+
} as const;
|
|
2266
|
+
|
|
2267
|
+
export type SendSolanaTransactionBody = {
|
|
2268
|
+
/** The Solana network to send the transaction to. */
|
|
2269
|
+
network: SendSolanaTransactionBodyNetwork;
|
|
2270
|
+
/** The base64 encoded transaction to sign and send. This transaction can contain multiple instructions for native Solana batching. */
|
|
2271
|
+
transaction: string;
|
|
2272
|
+
};
|
|
2273
|
+
|
|
2274
|
+
export type SendSolanaTransaction200 = {
|
|
2275
|
+
/** The base58 encoded transaction signature. */
|
|
2276
|
+
transactionSignature: string;
|
|
2277
|
+
};
|
|
2278
|
+
|
|
2255
2279
|
/**
|
|
2256
2280
|
* The token to request funds for.
|
|
2257
2281
|
*/
|
|
@@ -13,6 +13,7 @@ import type {
|
|
|
13
13
|
ExportSolanaAccount200,
|
|
14
14
|
ExportSolanaAccountByName200,
|
|
15
15
|
ListSolanaAccounts200,
|
|
16
|
+
SendSolanaTransaction200,
|
|
16
17
|
SignSolanaMessage200,
|
|
17
18
|
SignSolanaTransaction200,
|
|
18
19
|
SolanaAccount,
|
|
@@ -203,6 +204,13 @@ export const getSignSolanaMessageResponseMock = (
|
|
|
203
204
|
overrideResponse: Partial<SignSolanaMessage200> = {},
|
|
204
205
|
): SignSolanaMessage200 => ({ signature: faker.string.alpha(20), ...overrideResponse });
|
|
205
206
|
|
|
207
|
+
export const getSendSolanaTransactionResponseMock = (
|
|
208
|
+
overrideResponse: Partial<SendSolanaTransaction200> = {},
|
|
209
|
+
): SendSolanaTransaction200 => ({
|
|
210
|
+
transactionSignature: faker.string.alpha(20),
|
|
211
|
+
...overrideResponse,
|
|
212
|
+
});
|
|
213
|
+
|
|
206
214
|
export const getListSolanaAccountsMockHandler = (
|
|
207
215
|
overrideResponse?:
|
|
208
216
|
| ListSolanaAccounts200
|
|
@@ -432,6 +440,29 @@ export const getSignSolanaMessageMockHandler = (
|
|
|
432
440
|
);
|
|
433
441
|
});
|
|
434
442
|
};
|
|
443
|
+
|
|
444
|
+
export const getSendSolanaTransactionMockHandler = (
|
|
445
|
+
overrideResponse?:
|
|
446
|
+
| SendSolanaTransaction200
|
|
447
|
+
| ((
|
|
448
|
+
info: Parameters<Parameters<typeof http.post>[1]>[0],
|
|
449
|
+
) => Promise<SendSolanaTransaction200> | SendSolanaTransaction200),
|
|
450
|
+
) => {
|
|
451
|
+
return http.post("*/v2/solana/accounts/send/transaction", async info => {
|
|
452
|
+
await delay(0);
|
|
453
|
+
|
|
454
|
+
return new HttpResponse(
|
|
455
|
+
JSON.stringify(
|
|
456
|
+
overrideResponse !== undefined
|
|
457
|
+
? typeof overrideResponse === "function"
|
|
458
|
+
? await overrideResponse(info)
|
|
459
|
+
: overrideResponse
|
|
460
|
+
: getSendSolanaTransactionResponseMock(),
|
|
461
|
+
),
|
|
462
|
+
{ status: 200, headers: { "Content-Type": "application/json" } },
|
|
463
|
+
);
|
|
464
|
+
});
|
|
465
|
+
};
|
|
435
466
|
export const getSolanaAccountsMock = () => [
|
|
436
467
|
getListSolanaAccountsMockHandler(),
|
|
437
468
|
getCreateSolanaAccountMockHandler(),
|
|
@@ -443,4 +474,5 @@ export const getSolanaAccountsMock = () => [
|
|
|
443
474
|
getExportSolanaAccountByNameMockHandler(),
|
|
444
475
|
getSignSolanaTransactionMockHandler(),
|
|
445
476
|
getSignSolanaMessageMockHandler(),
|
|
477
|
+
getSendSolanaTransactionMockHandler(),
|
|
446
478
|
];
|